Modern Java – A Guide to Java 8
github.com
github.com
String foo = Optional.ofNullable(paramater)
.filter(...)
.map(a -> ...)
.map(b -> ...)
.orElseGet(() -> ...)
.orElse(defaultValue)
Between this kind of thing, and similar playing with Streams, some of the ugliest code I work with is suddenly quite clear, coherent, understandable.1 - http://mail.openjdk.java.net/pipermail/lambda-dev/2011-Septe...
Java probably followed Scala's notation, which is probably derived from mathematics where ↦ is an application (a function) and ⇒ is a simple logical implication.
Doesn't matter much in the long run, you'll get used to whichever your language favors after just a few days or writing code in it.
- An `public Optional<T> or(Optional<T> other)` so if optA is empty, then `optA.or(optB)` would return `optB`
- A `toStream` method on `Optional` (I believe this is coming in Java 9) or have a `Stream.flatMap` signature that flattens `Optional`s without needing to call `Optional.toStream` (Scala does this, arguable if this is an acceptable munge of Monadic types, but it's really convenient)
String foo = defaultValue;
if (parameter != null && ...) {
foo = doAAndB(parameter);
}Comparing your code and mine, I ask you this: which one do you thing is more maintainable after 5 years of edits?
Each time a new developer needs to add a new case, a new rule, a new whatever, on my Optional (ab)usage, they just add a new line in the appropriate spot, a .filter or a .map or whatever. In yours it's far less clear.
Optional, for me, is wonderful because it lets me have a single return statement. A lot of the code in Java I'm replacing with this pattern is the stuff that has 5 different returns after checks. Crusty old stuff, but the business logic it's running has grown the way it has for good reasons.
The biggest worry I have right now is the story on debugging code with Optional bastardized FP. I know I'm going to learn some lessons soon enough, lol.
Ceylon OTOH is at least good design, though I don't think that outweighs the library and tool ecosystem that Scala has.
The fact that there's so much discussion on the new SIP (Scala improvement process) and SLIP (Scala library improvement process) issues is a testament that Scala is alive and well. This is what happens in a mature language: many people put stakes in the ground.
Also, Scala most certainly has a bright future to go with its vibrant present. EPFL is hard at work with dotty, which will be a simplification and improvement of Scala.
Do standard collections have groupBy? You can go a long way with groupBy and zipWith operations, which are a pair like map-reduce but for in-memory handling.
I believe they are automatically combined before the results of the stream are forced.
#1- If I'm dealing with some legacy code that I don't know well, and it's mapping from one object to a property of it that is another object... well, I'm not 100% certain it's not null.
.map(a -> a.getB())
.map(b -> b.getC())
if a.getB() is null, Optional eats it and I'm safe from NPE- I just an a non present Optional value. .map(a -> a.getB().getC())
If B is null, I'm going to NPE here (... right? I'm like 99% sure of that, but correct me please). And there are counter arguments like "never be null", etc, but I've got legacy code to cleanup, and no one taught the person who wrote it that particular lesson.#2- Ease of understanding. Little jumps rather than big ones. Makes the code easier to understand for the next guy who has to work on it. As much as I can, I try to write code that requires no commenting- because it's so clean it's not needed.
#3- The compiler will (or will eventually) optimize it into a single map anyways. Compilers are smart and keep getting smarter. So long as my big-O runtime is fine, I just focus on ease of understanding of my code.
String foo;
if (parameter != null) {
foo = ... ;
}
else {
foo = defaultValue;
}
or even this, if you express all your transformations as a single expression: String foo = parameter != null
? ...
: defaultValue
I don't get it. Your way of doing seem awfully more verbose. Please note that
I'm not against Optional in general (although they are overplayed in my
opinion).Second thing, I can't shake off the feeling of inefficiency: building these closures up, calling them. Lots of indirection for something that's probably very simple. I wouldn't want to call this code in a hot loop. But then maybe it's not in a hot loop. Before you bring that up, the sufficiently smart (JIT) compiler doesn't exist. "Sometimes suprisingly dumb" is a better qualifier for many compilers.
Note that I work with Java, and use the Java 8 features a lot. I just think this isn't a really good example.
If you actually take the time to translate OP's code with your own, you will soon find yourself indented six levels with "if (a != null) { ... if (b != null) { ... if (c != null) {...
You get the idea.
Monads (the type class that Optional belongs to) flatten all this boiler plate with a function called... flatMap! And you don't even need to know about it, all you need to do is chain the calls like OP did:
Optional.ofNullable(paramater)
.filter(...)
.map(a -> ...)
.map(b -> ...)
.orElseGet(() -> ...)
.orElse(defaultValue)
without having to check against null every step along the way.Cliffnotes: I think I have discriminated against Java mostly on a "religious" basis. I think it's a great time for people like me to give it another shot (especially if you have grown accustomed to functional ideas form other languages in the meantime).
The repo is an excellent resource, bookmarked.
That's not exactly a point for Java though, considering it doesn't really have a good static type system.
(To answer your question, a type system should be sound unless and until the user explicitly requests otherwise, easy to use, but above all consistent)
Enum types didn't exist in Java when the Comparable interface was implemented. That's why some API's look old-school by comparison because newer language features didn't exist yet.
That could be stated about pretty much every feature added since Java 1 really, it's not like the blub paradox has disappeared from our reality. How do you know that you wanted union types when you've never used a language which had them (and had features hindering such a conceptualisation, C-style enums are not really helpful there, even less so when extended the way Java did)
My counterargument would be the popularity of annotation-based libraries in Java. An annotation is basically a place where the official type system has proven inadequate.
http://ceylon-lang.org/documentation/1.1/tour/types/
Compared to union/intersection types, I find myself craving higher kinded types less frequently. I do feel the absence strongly, however, when I'm writing tools. I think the lack of higher kinded types manifests in the Java community as inferior libraries (eg ORMs) that require more manual-feeding of information via annotations or extra parameters to methods.
When you become used to a given language, you start to reason in its terms. A programmer who is used to Java is used to its shortcomings and will not miss Lisp macros or dynamic types.
To be fair, I can think of another functional language starting with "O" guilty of exactly the same thing...
(This idiom is a bit of a historical error. It would be better if compare returned a variant with three cases for less than, greater than, and equal. But it's a well-established idiom at this point, and unlikely to change.)
Eiffel is similar to ocaml : http://www.infor.uva.es/~felix/referencia_smart_eiffel/libra...
Does anyone know the advantages of the above signature over the Haskell alternative? Because this seems to be more obvious and easier to read. So there are more than one language guilty of using a ternary operator for compare.
Haskell does define the appropriate constructors, rightly so: https://hackage.haskell.org/package/base-4.8.1.0/docs/Data-O...
data Ordering :: *
Constructors LT EQ GT
Historical reasons, mostly. The only practical advantage I can imagine is that it's a bit shorter to implement a reverse comparison, since you just multiply by -1.
Many of us in the 90's jumped into Java, in spite of its issues, because it made it more pleasing to write cross platform code, than using C or C++ with CORBA/COM across OSes with compilers that were still playing catchup with the standards.
I am still programming in java in my day job, knowing fully well that Java was not my top five language choices. Eiffel's type system was a bit more advanced than Java even way back in those days, so not only was I stuck without an ide till VA for Java showed up or VisuallJ++ I also had to accept weak inheritance and design by contract models. Performance of course was not even close to an Eiffel compiled C code, which was probably addressed in later versions of java.
My point being, that just as Eiffel was something that was better and set aside by a few leading development shops, there must have been other languages that could/should have received a fair share in language evaluation by programmers, I dont necessarily mean limiting to Ruby, Python for example, despite them being excellent tools, they may fall short in an enterprise ecosystem. What happened at least I seem to think instead is a sort of groupthink to start coding in Java because of rubbernecking. All in all, I think that things could have been better if people paused to understand the Java language model and fixed in java 1.2/3, or some earlier version so we didnt have to wait till Java 1.8 to get these features. /rant.
Before Java was known to the world, I already knew plenty of languages with closures, value types, GC,AOT compilation to native code, ...
But they all suffered from two problems, not being from known companies with geek credit and being commercial.
Java was from Sun (geek credit) and was free (as in beer), hence why it got adopted.
I think it would been great if Eiffel had got a decent market share,instead of just the DBC ideas.
But if I remember correctly they went after the enterprise, so the language was out of reach to many developers given what they used to charge for.
If you are using Java and have not tried Lambda Expressions, method references and default methods, do so now! After that, you'll think "How was I able to live without that?".
I also think that the way Java integrates lambda expressions (via functional interfaces) is really nice. Sure, you have to write some boilerplate code in some scenarios. But since they re-used interfaces and did not event some "function" type (or similar), it fits really nicely into the existing language.
If you don't know Java, this tutorial will probably not be enough - But that probably wasn't the intentian anyway. It's a great intro to what has changed with Java 8 (and all those changes are real improvements for the Java language, IMHO).
I think instead of "How was I able to live without that?", people are more likely to exclaim "about time!"
coll := ((1 to: 100) select: [:x| ( x * x ) > 3]) collect: [:x | (x * 2)].Also, I was once co-teaching a C# workshop where we showed people how to work with LINQ and extension methods (long, long time ago ;) ). Some participants asked us if there was a way to configure the compiler to forbid those features for their team.
Not everybody likes "modern" features, I guess ;)
Very little LINQ usage, usually Java 6 still the latest version on the servers, C++ code older than ANSI C++98....
Which is mostly the point. Ultimately, everything is no different from <some lower tech thing doing the same thing>.
This is very misleading. There is not a #stream method on Map because Map supports three different streams:
map.keySet().stream()
map.values().stream()
map.entrySet().stream() map.entrySet().stream().filter(e -> e.getKey().equals(e.getValue())).findAny()
is not expressible.keySet() and entrySet() return Set-conforming objects because they can, values() can't and only returns a Collection, but these three objects delegate much of their logic to the map itself.
m.forEach((key, value) -> System.out.println("key: " + key + " value: " + value));
https://github.com/winterbe/java8-tutorial/blob/master/READM...
Side note: http://winterbe.com looks quite similar to angel.co. Or does angel.co look quite similar to your website? :-)
Java 8 is such a huge improvement over previous versions. One thing it still lacks is a better literal format for defining maps, etc.
I just self published a book that uses Java 8 (https://leanpub.com/powerjava if you will pardon a plug) and to be honest I got some pushback from a few people who like my books as to why I didn't use Clojure, Haskell, etc. To be honest I do prefer Clojure and Haskell, but the Java ecosystem is so huge, with so many good libraries that sometimes it is the more practical language to use.
For literal maps, I tend to use double brace initialization style. It is more verbose than languages with direct literal support for maps and creates an extra anonymous class, but it keeps the map in a single syntactic construct. For lists, I use Arrays.asList(a,b,c) style; if you static import the method it reads nicely as-list-a-b-c.
It is usually presented as anti-pattern in talks about Android performance.
Map<Foo, Bar> map = ImmutableMap.of(foo1, bar1, foo2, bar2, foo3, bar3, foo4, bar4);
There are overloads for up to 5 entries, and they're even key/value typesafe.For Java code, it's often easier to predict the performance characteristics of a part of the code. But you shouldn't trust your judgements anyway - instead use a profiler - so this is not really a reason.
Speaking of performance: I once talked to someone working for Elasticsearch, and they told me that they cannot use clojure for most of their code because of their performance requirements. Clojure collections are almost as fast as Java collections in many scenarios, but that is apparently not fast enough for them. This is probably not a valid reason for most projects, though.
There is a huge number of Java developers out there, and Java is easier to learn for someone coming from C++ or C# or JavaScript than clojure (at least I am pretty sure about that). So hiring cheap developers might be easier if you use Java. You might count that one as a political statement.
There are some great tools for Java out there: IntelliJ Idea, Yourkit Profiler, JRebel, ... Cursive Clojure is great, but IntelliJ for Java has even more integrated workflows.
There are very mature open source libraries / frameworks / tools for Java: JUnit, Spring, Guice, Dropwizard, Gradle, Elasticsearch, ... You can have commercial support for many of them. Most of the libraries I use with clojure are version 0.3 or something like that with no possibility for commercial support (but they work fine anyway).
Static typing: A lot of people complain about it, but it can really help you to create maintainable code. See for example http://talks.samirtalwar.com/use-your-type-system.html
It's not some kind of war or personal affront to you - some people simply prefer the Algol style to the Lisp one. This even includes people who are very fluent in the Lisp style.
Clojure and Scala exist because of the JVM, not because of Java. Java 8 will or won't be an interesting language independently of the JVM, oddly enough.
In order to adopt a language that isn't in the top 6 or so, you've got to have good reasons for it. And depending on the problems you're trying to solve, sometimes it's worth it, sometimes it isn't. For a startup, with a small but capable team that's trying to outrun its competition to market, then using the most productive tools possible is a good choice. In other contexts though, like in a big company where long-term maintainability might be more important than speed to market, well, Java might be the better choice, and not because of the language per se (I think Java code is often unreadable), but more because the cost of maintainability is low due to its popularity and its slow pace of change.
I'm not saying Java is good looking by any means, but I've never found it to be heard to read in the "I can't completely grasp exactly what everything is supposed to do" kind of way. I often find Python and JS to have enough shortcuts, callbacks, and anonymous functions to find other people's code much harder to grok than other people's Java.
Not bad_user, obviously, but excessive verbosity can be antithetical to readability. (As can excessive conciseness.)
It can make the overall structure and meaning of the code hard to discern, even though can clearly tell where what gets assigned to what variables, etc.
Were popularized in the PC, Amiga and NeXT eco-systems, thanks to Turbo Pascal, Delphi, VB, AMOS, GFA, Objective-C, Eiffel, Oberon, ...
Java wasn't even a thing in those days.
Which is very hard in languages that are a pile of hieroglyphics.
This is very important in teams of 50+ developers, scattered around countries with high attrition rate, having various skill levels.
Also generating boilerplate is just 1% of what an IDE is capable of.
Personally I don't understand why some programmers insist on communicating only in english words. First of all because english words suffer from the problems of natural language, which is that natural language is imprecise, with the words having multiple meanings depending on surrounding context. For example when adding two numbers, should you use "add" or should you use "plus"? As it may be, "add" is actually incorrect according to its precise English definition, because the operation doesn't necessarily lead to increasing the size or amount. Yet this does not stop people from using it. Fortunately classes like BigDecimal are using "plus", yet I don't get why in the world would anybody think that "x.plus(y.multiply(z))" is more readable than "x + y * z".
You might thing picking on BigDecimal is a cheap shot. Well, how about the well known "ListUtils.union(a, b)" and why would that be better than "a ++ b". Speaking of "ListUtils.union", joining 2 lists is not a union, as union is in the context of sets or maps. Joining 2 lists is a concatenation and that matters, because concatenation is not commutative, whereas a union (of 2 sets or maps) is commutative. Basically the Apache Commons folks have got the naming wrong and if such mistakes happen in libraries that are so public, guess what happens inside corporate projects.
But much more problematic in languages like Java is not the wording as much as the way the logic is often expressed. In a functional programming language you usually get pure expressions that operate on immutable data-structures, pure as in true mathematical functions (for the same input you always get the same output). Such functions are much easier to reason about and much easier to test, because the output does not depend on some object's history (or in other words, an object's identity).
The worst and most unreadable code I've ever seen was written in Java, a language in which people often pass around mutable data-structures, like arrays, modifying them on the spot, for no good reason other than not knowing any better, in a dance of mutation that can only be considered an abomination of nature, with code so obtuse that it would make grown men cry. It's not uncommon for pieces of code to be commented with "here be dragons" with people no longer understanding what it does. Couple that with "enterprise design patterns" based primarily on IoC containers and best practices spawned from hell, with deep layers of inheritance that don't make sense and with chronic multi-threading issues and you've got a recipe for disaster. And you don't even have to search for proof for too long. Take any reasonably sized open-source project and you'll see this fact in all its glory. The last Java open-source project I interacted with is SpyMemcached and is a fine example of a Java project that works, that does its job well, that is reasonably well maintained, but that has internals that expose all the problems that I just mentioned. And sometimes I wonder why anything written in Java works at all, my guess being simply that people hit that code with the hammer until it quacks, in a process that resembles more natural evolution and mutation rather than engineering.
Therefore I'm personally dumbfounded by claims of readability. And in regards to the capabilities of an IDE, I do use IntelliJ IDEA, but I am wondering what the other 99% of its capabilities are, preferably that help with readability. Syntax highlighting?
You see, Go is not popular enough to have users that don't like it. The people that end up trying out Go simply move to something else if they don't like it. Go's community is also strongly opinionated and has rejected any dialog for meaningful improvements. In other words the Go community is filtering out people that want something different. Whether this is good or bad, you be the judge, but if there's one thing that's definitely bad is the echo chamber. Case in point you're under the impression that Go is tolerable, even though many of us consider it to be worse than Java.
On the contrary, I think the recent additions from Java 8 are a step in the right direction.
Except almost all the tools work with the pre-java-8 way of doing things (and often even pre-java-7). Thus, you end up with layers of different idioms, all of which are not quite compatible with each other.
If you are stuck with a group of developers who have no interest in learning anything new and wish to use Java until they retire then Java is the only choice. Other than no new learning required I can't think of many reasons to pick Java as a first choice. Java tends to be popular because it's the oldest, most familiar default for the JVM rather than any particular strength.
Put another way: if you're on a dev team where nobody knows Clojure/Scala, and everyone knows Java, (and, optionally, the codebase is already in Java), and your team doesn't have time for everyone to learn an entirely new language (including cleaning up the beginner's mistakes that come with that process), then Java's the better choice.
Don't get me wrong, Java grinds my gears sometimes, and I frequently wish I could use Kotlin or F# or some kind of typed Ruby in my Java-shop workplace, and I enjoy picking up the cool ideas from languages like Idris (dependent types are really cool!) in my free time. But I'm sympathetic to my coworkers in the environment where I work: 4 devs in the whole company, startup pressure to get to a break-even point so we don't go under, and a backlog longer than all of us could finish this year even if we froze it now.
Sometimes circumstances require what looks like a short-term decision in order to ensure that there's a long term to worry about later.
I bet you will find thousands of C++ devs that don't even know that there are newer versions of C++98.
C devs that still use K&R as their guide with C89 code style.
And so on.
We are the few select ones that care to improve our skills.
EDIT: Typos.
Actually had a guy tell me (in 2006!) that the main problem with C++ was that there was no standard. :|
There are however areas where I'd use clojure every time. Luckily the JVM is really good for doing mixed language work on, so you don't have to work entirely in one language.
Clojure has a weakly integrated type system (an inherent disadvantage of optional type systems). At the simplest level this makes silly errors much easier and means you have to write more tests to maintain the same defect rate.
The lack of types mean you require extensive use of macros for advanced functionality. IME macros have major maintainability issues in a multi-person codebase.
Both these things are major disadvantages for automatic comprehensibility of code. Autocompletion can be more-or-less usable but will never be as good as in Java or Scala. Automated refactoring is inherently unsafe in the presence of macros (Scala's fancier typed constructs (typeclasses, for/yield with custom types) mean you need macros much less often; Java tends to force you to expand these things out by hand (or else use annotations which act as de facto macros), which has its own maintainability issues but does at least mean automated refactoring will work correctly).
Some of the language culture pushes people towards less principled abstractions. From this side of the fence that looks like anti-intellectualism; no doubt from their side it's pretension on typed programmers' part. But either way I think they're setting themselves up for long-term maintainability issues (e.g. the semantics of clojure transducers in the presence of errors are infuriatingly not-quite-right, which will either remain a painful gotcha forever, or necessitate a painful migration in the future).
As a minority language Clojure may not be as well supported in the surrounding ecosystem - partly things like IDEs but also code coverage tools, profilers, monitoring.... Remember the JVM ecosystem is wider than just the languages themselves.
There are good things about Clojure, but it's by no means clear-cut.
In terms IDEs, Cursive for IntelliJ is great, and it gives almost Java-like capabilities (with limitations inherent to more dynamic languages, of course).
I have heard this a bunch, and I believe it, but I've never personally had an opportunity to use a language that supports macros for a multi-person codebase. I'd be interested to learn what are some of the pitfalls you've seen, if you don't mind sharing.
Lisp got a lot of things right but I'm not sure dynamic typing and s-expression syntax were among them.
Could you elaborate?
So you need to be comfortable with it, as there will always be cases you need to jump out of the alternative language into Java.
Also, most companies will only hire to work with Java on the JVM, because they don't want to be hostage of the cool language some of the dev team guys/contractors decided to use instead.
It is very rare to see traditional Java shops to use alternative languages.
Usually the ones using them are either startups or companies like Facebook and Twitter, which aren't the typical business culture.
They're conservative about adding features, so they've managed to keep the language pretty small, and the core concepts of "what is good Java code" have been mostly the same for like 15 years, even through major releases like 1.5.
Java developers usually consider features which the language doesn't have to be bad or make code unreadable, that is until those same features are added to Java, when suddenly they are a source of pride and a sign of Java being "modern". In reality Java's feature freeze until Java 8 was largely driven by Sun's financial woes.
Map<String, Customer> customerMap = createCustomerMap();
where in other less verbose languages you find val customerMap = createCustomerMap();
Always seeing the definition of a value in the current function context is actually very nice for maintainability, but does give Java the reputation of being overly verbose.Stuff like getters/setters on all values in a class are awful though and are both a code smell and unnecessary. They break the OO principles and should not be there in the first place. It's good that Java makes those 6 screens worth of getters and setters awful - they are awful. For objects which are used solely for transfering state, check out Google's AutoValue ( https://github.com/google/auto/tree/master/value )
However if you do see a class that is 6 screens of just getters and setters then you know exactly which class you need to fix.
There are also many less favourable cases where you see things like:
Customer customer = new Customer();
String str = "I'm a String"
SomethingOrOtherFactory factory = new SomethingOrOtherFactory();
which isn't any clearer than: val customer = new Customer();
val str = "I'm a String"
val factory = new SomethingOrOtherFactory()
Having a dozen getter/setters is generally a code smell, but having a dozen getters isn't. Animal customer = new Cat();
It makes it clear how the new object will be used. It does come up quite frequently, especially with Collection and List. Nobody was saying it is not verbose and can lead to boilerplate. The assertion is that when viewing a large code base, you are always sure of the type of value you are dealing with on a local level. If all of your objects are well defined, you don't even need to look at how the class is created (a billion getters and setters? irrelevant to the local function) as you only need to deal with the local function. val customer = new Cat(): Animal
I can see arguments against inter-method type inference, but I think there's no real argument against type inference within a single method (requiring explicit types on method boundaries). If you have a billion-line method you have bigger problems.Comparing to the original implementation of properties in C# (not how they look now), the Java way wasn't that different.
They just copied what was common C++ and Smalltalk practice back then, whereas Anders obviously had his Delphi experience.
Not that they shouldn't eventually improve it.
I would suggest just keep using Clojure and when it makes sense, mix in Java when you want or need to.
If you want a dynamic language on the Java platform; then why not use JavaScript?
There is an easy-to-use engine in Java 8 and JavaScript is widespread and somehow familiar to Java-developers.
The main choice is then either Clojure or Scala. This comes down to dynamic versus static type checking. With a dynamic language, one is significantly less sure if a program is correct. In large programs that use dynamic languages, many tests must be created that are essentially doing the work that a type checker in a static language would do automatically.
In a static language, entire classes of bugs are eliminated. Scala provides the best support for strong, static typing on the JVM.
I'd argue that Ceylon and Kotlin offer most of Scala's advantages with none of its baggage and bloat. And drama.
Kotlin and Ceylon both have very good IDE plug-ins that already work much better than Scala, despite Typesafe pouring a lot of money into their own plug-in.
You can see an introduction on this Oracle documentation: https://docs.oracle.com/javase/8/docs/technotes/guides/scrip...
I'm actually in the process of putting together a presentation on how to work magic with streams (because I'm one of the few people here who actually knows how to use them), and this will probably get a mention in a "further reading" slide, because it's really well written.
It's also inspired me to make a few slides focusing on the changes to Map and Comparator, which I wasn't aware of until now.
I have been writing Java code since 6 years. Although I can't say that I have programmed in many other languages (I have coded in C/C++ and Javascript), Java has been always very comfortable to use. It's easy to think of a solution in the Object Oriented paradigm, and I think refactoring (when a requirement changes) has not been much of a problem for me. It's only bad designs that make the code more verbose than it should be, and I think verbosity is actually a good way to articulate your thoughts, and see them evolve in front of you. Recently when I started learning Haskell a bit, I found that there are much faster and concise ways of writing code, but I still prefer Java because of the clarity of the way it allows me to express my logic. People would say that writing a line of code in Haskell would do what 10 lines of code in Java would do, but in the long run, I have found that expressing programming paraphernalia like interfaces and classes actually helps the coder in his job (not necessarily results in wastage of time).
E.g. if you're coming from groovy/Scala and wonder "why doesn't Stream have this method?", Seq probably either already has it, or the maintainer will be open to adding it for you.
So, use jOOL. It's great.
That said, I still cringe that we have to do "someCollection.stream()..." (or use fill-ins like jOOL) at all, because these methods aren't on j.u.List/etc. itself...
My naive impression is that the JDK designers fixated on making streams support parallelization (because fancy!), and so made some compromises on the API, when in reality 98% of collections are small/not parallel, and I assert a non-parallel, more complete API (e.g. more default methods directly on j.u.List/j.u.Iterable themselves) would have been a net-win to most programmers.
And the parallelized version could be a separate library/jar/something.
> static <T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> Seq<Tuple16<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>> crossJoin(...
[0] https://msdn.microsoft.com/en-us/library/dd402872(v=vs.110)....
[1] https://github.com/rust-lang/rust/blob/master/src/libcore/tu...
[2] https://downloads.haskell.org/~ghc/7.2.2/docs/html/libraries...
I've done more OO development in C++, and I've found this technique useful - putting a default implementation in a class which was mostly an interface because only a small number of subclasses needed specialized behavior.
[1] https://docs.oracle.com/javase/8/docs/api/java/util/concurre...
Anyway, the major difference is that Future.get throws 2 checked exceptions, InterruptedException and ExecutionException, while CompletableFuture.join does not throw any checked exception. Instead, it wraps any exceptions in CompletionException.
[1] https://docs.oracle.com/javase/7/docs/api/java/util/concurre...
Why is that?
They make use of invokedynamic, there is no expansion taking place as many think.
http://www.infoq.com/articles/Java-8-Lambdas-A-Peek-Under-th...
(define-syntax stream-cons
(syntax-rules ()
((_ x xs)
(cons x (delay xs)))))
(define (stream-cdr xs)
(force (cdr xs)))
(define (stream-map f xs)
(stream-cons (f (car xs)) (stream-map f (stream-cdr xs))))
(define (stream-filter f xs)
(if (f (car xs))
(stream-cons (car xs) (stream-filter f (stream-cdr xs)))
(stream-filter f (stream-cdr xs))))
(define (stream-take n xs)
(if (<= n 0)
'()
(cons (car xs) (stream-take (- n 1) (stream-cdr xs)))))
(define (repeat x)
(stream-cons x (repeat x)))
(define (iterate f x)
(stream-cons x (iterate f (f x))))
(define (replicate n x)
(stream-take n (repeat x)))
(stream-take 5 (stream-filter odd?
(stream-map (lambda (x) (* x x)) (iterate (lambda (x) (+ x 1)) 1))))
Not mentioning all these different flavors of Common Lisp streams packages.As for other features, Scala is around for almost a decade.
The point was that if one has a proper old-school computer science (to realize the crucial importance of first class procedures and the power of uniformity 20 years ago) all these modern features are coming for free, to which the code above is an illustration.
But who cares. There is a whole industry which praised Java for almost two decades even without these "modern features".
Any PL student of a decent school, if he is not a hypocrite, would tell you that Java is the worst thing that happened to CS since MS DOS, but who cares about PL theory or even CS? Availability bias and Cargo Cult is enough.
But then LISPs have always been shunned for bad reasons. While crazy bad languages like C,Java and PHP are picked up by the masses.
Let's name it as The Law of a Decent Runtime, and The Law of Attention to Details, and The Law of a Bazar of Ignorant.))
The Law Of a Decent Runtime is very simple - evolving a decent runtime is very costly and time-consuming. It is also related to the second law and to the inverse of the third - which is a the Law of Dictature of The Most Competent. A decent runtime cannot be produced without talent, time, financing, competence and attention to details.
The examples are what came out of Xerox Parc, Bell labs, Ericsson and the best parts of academia (MIT Scheme culture, Scala, Standard ML, LLVM and Haskell, monads aside).
There are also many in-house (Tensorflow) and sponsored open source porjects (LLVM, Golang, Julia, Torch, to name a few). Basically, it is about resources spent on a talent.
What is important distinction - a decent runtime cannot be produced by the Bazaar of Ignorant (PHP, amateur Java code, SAP, and other "fractals of a bad design").
The Linux kernel is very special example, because it combines the law of big numbers, and all these three - it is a product of a whole planet of competent volunteers and paid developers - unlike PHP or Java ecosystem there is very high barrier to entry, thanks to The Dictature of The Most Competent .)
The law of Attention To Details - is quite obvious, and related to the first. This is why most of successful projects had a passionate and competent leader who sets the standards, be it Linux kernel, Erlang, Nginx, Gambit Scheme, Python, OpenBSD, Redis, Scala, SBCL, PostgreSQL, you name it.
Popularity has nothing to do with it. It is based on the principle of instant gratification (PHP, MongoDB) or Availability Bias boosted by paid content brainwashing (Java, SAP, MS) without understanding and preferably any thinking. Popularity doesn't mean quality at all, be it junkfood or PHP.
http://the-programmers-stone.com/the-original-talks/day-1-th...
That's why we don't listen to students to make this kind of uninformed claims.
Java has flaws, for sure, but calling it the "worst thing since MS DOS" can only come from someone who lacks perspective and experience.
http://the-programmers-stone.com/the-original-talks/day-1-th...
It's a matter of time until your team lead takes a glance at the code-base, deems them unreadable and "too clever" (as in "where am I going to find cheap developers now?") and makes you revert all the way back to Java 6.