Java 8 Friday: Language Design is Subtle
At Data Geekery, we love Java. And as we’re really into jOOQ’s fluent API and query DSL, we’re absolutely thrilled about what Java 8 will bring to our ecosystem.
Java 8 Friday
Every Friday, we’re showing you a couple of nice new tutorial-style Java 8 features, which take advantage of lambda expressions, extension methods, and other great stuff. You’ll find the source code on GitHub.
Language Design is Subtle
It’s been a busy week for us. We have just migrated the jOOQ integration tests to Java 8 for two reasons:
- We want to be sure that client code compiles with Java 8
- We started to get bored of writing the same old loops over and over again
The trigger was a loop where we needed to transform a SQLDialect[]
into another SQLDialect[]
calling .family()
on each array element. Consider:
Java 7
SQLDialect[] families = new SQLDialect[dialects.length]; for (int i = 0; i < families.length; i++) families[i] = dialects[i].family();
Java 8
SQLDialect[] families = Stream.of(dialects) .map(d -> d.family()) .toArray(SQLDialect[]::new);
OK, it turns out that the two solutions are equally verbose, even if the latter feels a bit more elegant.
And this gets us straight into the next topic:
Backwards-compatibility
For backwards-compatibility reasons, arrays and the pre-existing Collections API have not been retrofitted to accommodate all the useful methods that Streams now have. In other words, an array doesn’t have a map()
method, just as much as List
doesn’t have such a method. Streams and Collections/arrays are orthogonal worlds. We can transform them into each other, but they don’t have a unified API.
This is fine in everyday work. We’ll get used to the Streams API and we’ll love it, no doubt. But because of Java being extremely serious about backwards compatibility, we will have to think about one or two things more deeply.
Recently, we have published a post about The Dark Side of Java 8. It was a bit of a rant, although a mild one in our opinion (and it was about time to place some criticism, after all the praise we’ve been giving Java 8 in our series, before ). First off, that post triggered a reaction by Edwin Dalorzo from our friends at Informatech. (Edwin has written this awesome post comparing LINQ and Java 8 Streams, before). The criticism in our article evolved around three main aspects:
- Overloading getting more complicated (see also this compiler bug)
- Limited support for method modifiers on default methods
- Primitive type “API overloads” for streams and functional interfaces
A response by Brian Goetz
I then got a personal mail from no one less than Brian Goetz himself (!), who pointed out a couple of things to me that I had not yet thought about in this way:
I still think you’re focusing on the wrong thing. Its not really the syntax you don’t like; its the model — you don’t want “default methods”, you want traits, and the syntax is merely a reminder that you didn’t get the feature you wanted. (But you’d be even more confused about “why can’t they be final” if we dropped the “default” keyword!) But that’s blaming the messenger (where here, the keyword is the messenger.)
Its fair to say “this isn’t the model I wanted”. There were many possible paths in the forest, and it may well be the road not taken was equally good or better.
This is also what Edwin had concluded. Default methods were a necessary means to tackle all the new API needed to make Java 8 useful. If Iterator
, Iterable
, List
, Collection
, and all the other pre-existing interfaces had to be adapted to accommodate lambdas and Streams API interaction, the expert group would have needed to break an incredible amount of API. Conversely, without adding these additional utility methods (see the awesome new Map methods, for instance!), Java 8 would have been only half as good.
And that’s it.
Even if maybe, some more class building tools might have been useful, they were not in the center of focus for the expert group who already had a lot to do to get things right. The center of focus was to provide a means for API evolution. Or in Brian Goetz’s own words:
@breandan @lukaseder Hopefully it will make a few people realize "gee, this language design stuff is more subtle than I thought."
— Brian Goetz (@BrianGoetz) May 6, 2014
Reaching out to the community
It’s great that Brian Goetz reaches out to the community to help us get the right picture about Java 8. Instead of explaining rationales about expert group decisions in private messages, he then asked me to publicly re-ask my questions again on Stack Overflow (or lambda-dev), such that he can then publicly answer them. For increased publicity and greater community benefit, I chose Stack Overflow. Here are:
- What is the reason why “final” is not allowed in Java 8 interface methods?
- What is the reason why “synchronized” is not allowed in Java 8 interface methods?
The amount of traction these two questions got in no time shows how important these things are to the community, so don’t miss reading through them!
“Uncool”? Maybe. But very stable!
Java may not have the “cool” aura that node.js has. You may think about JavaScript-the-language whatever you want (as long as it contains swear words), but from a platform marketing perspective, Java is being challenged for the first time in a long time – and being “uncool” and backwards-compatible doesn’t help keeping developers interested.
But let’s think long-term, instead of going with trends. Having such a great professional platform like the Java language, the JVM, the JDK, JEE, and much more, is invaluable. Because at the end of the day, the “uncool” backwards-compatibility can also be awesome. As mentioned initially, we have upgraded our integration tests to Java 8. Not a single compilation error, not a single bug. Using Eclipse’s BETA support for Java 8, I could easily transform anonymous classes into lambdas and write awesome things like these upcoming jOOQ 3.4 nested transactions (API not final yet):
ctx.transaction(c1 -> { DSL.using(c1) .insertInto(AUTHOR, AUTHOR.ID, AUTHOR.LAST_NAME) .values(3, "Doe") .execute(); // Implicit savepoint here try { DSL.using(c1).transaction(c2 -> { DSL.using(c2) .update(AUTHOR) .set(AUTHOR.FIRST_NAME, "John") .where(AUTHOR.ID.eq(3)) .execute(); // Rollback to savepoint throw new MyRuntimeException("No"); }); } catch (MyRuntimeException ignore) {} return 42; });
So at the end of the day, Java is great. Java 8 is a tremendous improvement over previous versions, and with great people in the expert groups (and reaching out to the community on social media), I trust that Java 9 will be even better. In particular, I’m looking forward to learning about how these two projects evolve:
Although, again, I am really curious how they will pull these two improvements off from a backwards-compatibility perspective, and what caveats we’ll have to understand, afterwards.
Anyway, let’s hope the expert groups will continue to provide public feedback on Stack Overflow.
Reference: | Java 8 Friday: Language Design is Subtle from our JCG partner Lukas Eder at the JAVA, SQL, AND JOOQ blog. |