Core Java

6 Reasons Not to Switch to Java 8 Just Yet

DukeXray

Java 8 is awesome. Period. But… after we had the chance to have fun and play around with it, the time has come to quit avoiding the grain of salt. All good things come with a price and in this post I will share the main pain points of Java 8. Make sure you’re aware of these before upgrading and letting go of 7.

1. Parallel Streams can actually slow you down

Java 8 brings the promise of parallelism as one of the most anticipated new features. The .parallelStream() method implements this on collections and streams. It breaks them into subproblems which then run on separate threads for processing, these can go to different cores and then get combined when they’re done. This all happens under the hood using the fork/join framework. Ok, sounds cool, it must speed up operations on large data sets in multi-core environments, right?

No, it can actually make your code run slower if not used right. Some 15% slower on this benchmark we ran, but it could be even worse. Let’s say we’re already running multiple threads and we’re using .parallelStream() in some of them, adding more and more threads to the pool. This could easily turn into more than our cores could handle, and slow everything down due to increased context switching.

The slower benchmark, grouping a collection into different groups (prime / non-prime):

Map<Boolean, List<Integer>> groupByPrimary = numbers
.parallelStream().collect(Collectors.groupingBy(s -> Utility.isPrime(s)));

More slowdowns can occur for other reasons as well. Consider this, let’s say we have multiple tasks to complete and one of them takes much longer than the others for some reason. Breaking it down with .parallelStream() could actually delay the quicker tasks from being finished and the process as a whole. Check out this post by Lukas Krecan for more examples and code samples.

Diagnosis: Parallelism with all its benefits also brings in additional types of problems to consider. When already acting in a multi-threaded environment, keep this in mind and get yourself familiar with what’s going on behind the scenes.

2. The flip-side of Lambda Expressions

Lambdas. Oh, lambdas. We can do pretty much everything we already could without you, but you add so much grace and get rid of boilerplate code so it’s easy to fall in love. Let’s say I rise up in the morning and want to iterate over a list of world cup teams and map their lengths (Fun fact: it sums up to 254):

List lengths = new ArrayList();

for (String countries : Arrays.asList(args)) {
    lengths.add(check(country));
}

Now let’s get functional with a nice lambda:

Stream lengths = countries.stream().map(countries -> check(country));

Baam! That’s super. Although… while mostly seen as a positive thing, adding new elements like lambdas to Java pushes it further away from its original specification. The bytecode is fully OO and with lambdas in the game, the distance between the actual code and runtime grows larger. Read more about the dark side of lambda expression on this post by Tal Weiss.

On the bottom line this all means that what you’re writing and what you’re debugging are two different things. Stack traces grow larger and larger and make it harder to debug your code.

Something simple like adding an empty string to list turns this short stack trace:

at LmbdaMain.check(LmbdaMain.java:19)
at LmbdaMain.main(LmbdaMain.java:34)

Into this:

at LmbdaMain.check(LmbdaMain.java:19)
at LmbdaMain.lambda$0(LmbdaMain.java:37)
at LmbdaMain$$Lambda$1/821270929.apply(Unknown Source)
at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193)
at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.LongPipeline.reduce(LongPipeline.java:438)
at java.util.stream.LongPipeline.sum(LongPipeline.java:396)
at java.util.stream.ReferencePipeline.count(ReferencePipeline.java:526)
at LmbdaMain.main(LmbdaMain.java:39)

Another issue that lambdas raise has to do with overloading: since lambda arguments have to be cast into something when using them to call a method, and they can be cast to multiple types, it may cause ambiguous calls in some cases. Lukas Eder explains this with code samples right here.

Diagnosis: Just stay aware of this, the traces might be a pain from time to time, but it will not keep us away from them precious lambdas.

3. Default Methods are distracting

Default methods enable a default implementation of a function in the interface itself. This is definitely one of the coolest new features Java 8 brings to the table but it somewhat interferes with the way we used to do things. So why was this introduced anyway? And what not to do with it?

The main motivation behind Default Methods was that if at some point we need to add a method to an existing interface, we could do this without rewriting the implementation. Making it compatible with older versions. For example, take this piece of code from Oracle’s Java Tutorials where they add an ability to specify a timezone:

public interface TimeClient {
// ...
static public ZoneId getZoneId (String zoneString) {
try {
    return ZoneId.of(zoneString);
} catch (DateTimeException e) {
    System.err.println("Invalid time zone: " + zoneString +
    "; using default time zone instead.");
    return ZoneId.systemDefault();
    }
}

default public ZonedDateTime getZonedDateTime(String zoneString) {
    return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
    }
}

And that’s it, problem solved. Or is it? Default Methods mix up a bit the separation of interface and implementation. In the wrong hands, As if type hierarchies don’t tend to tangle up on their own, there’s this new creature now that we need to tame. Read more about it on Oleg Shelajev’s post on RebelLabs.

Diagnosis: When you hold a hammer everything looks like a nail, keep in mind to stick to their original use case, evolution of an existing interface when a refactor to introduce a new abstract class doesn’t make sense.

Moving on to some things that are either missing, still with us or not exactly there yet:

4. Wherefore art thou Jigsaw?

Project Jigsaw’s goal is to make Java modular and break the JRE to interoperable components. The motivation behind this first comes from a desire for a better, faster and stronger Java embedded. I’m trying to avoid mentioning the “Internet of Things”, but there I said it. Reduced JAR sizes, performance improvements and increased security are some more of the promises this ambitious project holds.

So where is it? Jigsaw entered Phase 2 just recently, passed the exploratory phase and is now switching gears to a production quality design and implementation, says Mark Reinhold, Oracle’s Chief Java Architect. The project was first planned to be completed in Java 8 and was deferred to Java 9, expected to be one of its flagship new features.

Diagnosis: If this is the main thing that you’re waiting for, Java 9 is due in 2016. In the meantime, take a closer look and maybe even get involved in the Jigsaw-dev mailing list.

5. Issues that are still around

Checked Exceptions

No one likes boilerplate code, that’s one of the reasons why lambdas got so popular. Thinking of boilerplate exceptions, regardless of whether or not you logically need to catch or have something to do with a checked exception, you still need to catch it. Even if it’s something that would never happen, like this exception that will never fire:

try {
    httpConn.setRequestMethod("GET");
} catch (ProtocolException pe) { /* Why don’t you call me anymore? */ }

Primitives

They are still here, and it’s a pain to use them right. The one thing that separates Java from being a pure Object Oriented language, criticized to have no significant performance hit for their removal. None of the new JVM languages has them, just saying.

Operator Overloading

James Gosling, the father of Java, once said in an interview “I left out operator overloading as a fairly personal choice because I had seen too many people abuse it in C++”. Kind of makes sense but there are lots of split opinions around this. Other JVM languages do offer this feature but on the other hand, it could result in code that looks like this:

javascriptEntryPoints <<= (sourceDirectory in Compile)(base =>
    ((base / "assets" ** "*.js") --- (base / "assets" ** "_*")).get
)

An actual line of code from the Scala Play Framework, ahm, I’m a bit dizzy now.

Diagnosis: Are these real problems anyway? We all have our quirks and these are some of Java’s. A surprise might happen in future versions and it will change, but backwards compatibility among other things is keeping them right here with us.

6. Functional Programming – not quite there yet

Functional programming has been possible with Java before, although it is pretty awkward. Java 8 improves on this with lambdas among other things. It’s most welcome but not as huge of a shift that was earlier portrayed. Definitely more elegant than in Java 7 but some bending over backwards is still needed to be truly functional.

One of the most fierce reviews on this matter comes from Pierre-yves Saumont where in a series of posts he takes a close look at the differences between functional programing paradigms and the way to implement them in Java.

So Java or Scala? The adoption of more functional modern paradigms in Java is a sign of approval for Scala who has been playing with lambdas for a while now. Lambdas do make a lot of noise, but there’s a lot more features like traits, lazy evaluation and immutables to name a few, that make quite a difference.

Diagnosis: Don’t be distracted by the lambdas, functional programming is still a hassle in Java 8.

Reference: 6 Reasons Not to Switch to Java 8 Just Yet from our JCG partner Alex Zhitnitsky at the Takipi blog.

Alex Zhitnitsky

Alex is an engineer working with OverOps on a mission to help Java and Scala developers solve bugs in production and rid the world of buggy software. Passionate about all things tech, he is also the co-founder & lead of GDG Haifa, a local developer group. Alex holds a B.Sc from the Technion, Israel's Institute of Technology.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

11 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
JAmes
JAmes
9 years ago

Not to mention that many static code analysis tools aren’t yet Java 8 friendly.

Dev
9 years ago

Thanks, Somehow I never had a soft corner for lambda expressions. Those are cool and really fun to play with however not sure what exactly it solves. Number of lines of code, is not issue for most of the people. May be scala seems too impressive with one liner but its use is still limited. Not many production deployments use it. I would care about features that speed up development and performance instead of cool looking ones. In general, I do not really prefer to jump on the bleeding edge until its been tried and tested out. your post helps.… Read more »

Yannick Majoros
Yannick Majoros
9 years ago

4/5/6 are just things you are missing. Why should this hold you from using Java 8? They aren’t there in 6 and 7, why stay?

Points 1/2/3 are about the fact that you can do dumb things. Should it really hold you from using lambdas in intelligent ways?

Andrew
Andrew
9 years ago

The other point that we do not have a code base with high-quality code examples.

Yannick Majoros
Yannick Majoros
9 years ago
Reply to  Andrew

Well, be part of the solution.

Andrew
Andrew
9 years ago

agree

Mike
9 years ago

I guess the title of this post is strictly hyperbole and was just supposed to be eye-catching, because there certainly aren’t 6 reasons here to avoid Java 1.8. 1) A valid thing to be aware of, but not a reason to avoid Java 1.8 2) A valid thing to be aware of, but not a reason to avoid Java 1.8 (if you don’t like lambdas just don’t use them) 3) I am supposed to avoid Java 1.8 because you don’t like default methods? API developers will probably love default methods. I think default methods are a reason to start using… Read more »

veggen
9 years ago

These are remarkably bad reasons for not using Java 8.

McPudding
McPudding
9 years ago

I expect better from JCG, than a post like this.

Michael Eric Oberlin
9 years ago

I have a lot of objections to your interpretation of these things as reasons not to use Java 8. It is not as though you cannot run a Java 7/6/5 program on a Java 8 platform, or interleave the styles of code. Your first three objections are, as stated before by someone else, questions of ethical programming, not faults in their presence. Your argument against a lack of Jigsaw is largely irrelevant (Jigsaw is not available in any other version of Java, either; what is your point?), your fifth is a total misdiagnosis of what qualifies as an issue instead… Read more »

Honza
Honza
8 years ago

Great, but you mismatched “country” and “countries” name of variable in second part. It should be one variable to make sense:

Stream lengths = countries.stream().map(COUNTRY -> check(COUNTRY));

Back to top button