6 Reasons Not to Switch to Java 8 Just Yet

By Alex Zhitnitsky —  July 9, 2014 — 16 Comments


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)) {

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. 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 {
} catch (ProtocolException pe) { /* Why don’t you call me anymore? */ }

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.

How was your experience with Java 8 so far? Did you start using it in production yet? Please let us know in the comments below.



CI – Know when your code slowed down after deploying a new version – read more



Already using Java 8? Try Takipi for Java 8


Alex Zhitnitsky

Posts Twitter

Some kind of monster @ Takipi, GDG Haifa lead.
  • arhan

    Number 4 is strange. Do you really think that Java 8 is not worth it without Jigsaw? Would be cool to read why Java devs need Jigsaw at all :)

    • Brandon G.

      Because modular programming has many benefits for larger apps and it would be nice to have modularity become standardized and official

      • arhan

        Sure. But in this post it is offered as a reason to stay on Java 7. Where’s the logic?

        • Brandon G.

          Sure. But in your reply it is suggested that Java devs have no need for Jigsaw at all. I’m just stating we in fact do. ;)

          • arhan

            Not that none of Java devs really need it, but I do have doubts that it is critical for an average business application. For app servers, it would certainly help as most of them are using OSGi or similar. So far I just haven’t seen any evidence that a module system would help to handle complexity of a business software application. The complexity is rather elsewhere.

          • Brandon G.

            I think that’s true in some cases, but not others. If you are thinking of a traditional, relatively small, business web-app, sure. But as complexity grows, the need can be argued. For example, I have a system with a collection of timed-scripts that interact with a variety of data-endpoints (database, directory servers) as well as scripts I don’t own interacting with those same endpoints to feed my scripts all fronted by a webapp that users use to feed my scripts as well. Once you start building out all the various layers for all of this, it gets pretty complex. In addition, you start bringing in tighter coupling and can’t modify/deploy without affecting the entire system at once, etc.

            On top of that, we routinely get requests to write a script that is almost the same as the ones we already have (but different end-point, slightly different business rules, etc); in the past we have C&P’d and created a whole new cron-job script. With modularity we are trying to arrive at a point where we can have a generic set of modules we can more dynamically piece together for that new functionality.

            Add to this known future path of slowly migrating some of these scripts away as we turn off their respective end-points in favor of different technologies… well… much easier to turn off a single module rather than comment-out big blocks of code, re-deploy a monolithic app, and pray.

            Kirk Knoerschild has a pretty compelling presentation if you get the chance.

          • arhan

            Thanks for the pointer. And yes, in theory, I understand the benefits. And there’s a good example of a real system that leverages modules – Eclipse. But in practice – so far even within a reasonably big business application (say, an internet banking app) I would hardly see the benefits. The most obvious way to break things into modules is vertically (by domain), not horizontally (by layer). And in that case microservices are a better fit, imo.

            In any case it goes far beyond the topic of the post. I just wanted to point that missing Jigsaw from Java 8 is not a reason to stay with Java 7 :P The same way as 5) and 6) ant really the reasons for avoiding the upgrade. And only 1) 2) and 3) are rather relevant reasons but in that case the developer would just stick with Java 7 forever :)

          • Brandon G.

            Actually I agree with your last para. I thought the reasons stated were good points, but didn’t at all fit the title of the post.

          • Brandon G.

            BTW I realized I ignored your suggestion for uServices…

            So in my project I’m using Karaf-OSGI to modularize. This brings a benefit of loose-coupling while also physically collocating related modules together. I also don’t require a web container just for a few modules to communicate amongst themselves. And if one module calls another that is temporarily down, the caller doesn’t throw up it just waits. Additionally, I can run different versions of the same module side by side, allowing my to test an upgrade in production while easily and very quickly rolling back if needed. This also gives me the ability to use different versions/implementations of 3rd party dependencies, so run time class path hell is gone.

            I’ve only written a couple uServices that were actually put in production, so admittedly I’m still learning, but these benefits are either really not possible with uServices or they at least are made more difficult.

            Also, with uServices you must do the lifting to handle the cross communication… shuttling and parsing the data. With modularization, all of the modules are loosely coupled by contract thus they can use objects to seamlessly shuttle data without parsing.

            One more big benefit from my POV is that uServices are either completely separate physical projects (only logically related, but a new Dev has to be made aware of their relationship) or they have to be contained in the same physical relationship (thereby allowing unaware Devs to mistakenly access code in other packages in ways that create undesirable dependencies. Modularization provides a container where the related modules can live together but in a way that each is independent of the other and it’s more difficult to mistakenly create bad dependencies.

            Finally, upgrading a single module doesn’t require I build and test the entire suite (which single-project uServices still would).

            All of these benefits, to accomplish via uServices requires overhead/complexity/knowledge. Course, so too does learning a modularity platform. Point is, you pick your poison. Each situation is different and each developer has different preferences. But I think it’s hard to say that there is a The Right Way. Time will tell of course, maybe in 3 years I will rue the day I wrote this project.

    • http://www.takipi.com/ Alex Zhitnitsky

      Thanks for the comment @arhan:disqus, great to see the discussion you opened up with @disqus_C7be6dbwzj:disqus. The Jigsaw project is one of the most anticipated features in Java, and although the announcement of its delay was way back in September 2012, this seemed like a good chance to mention it again. The purpose of this post is not to hold you off from upgrading to Java 8, more like an opportunity to keep you alert about some of its main caveats. Hope you find some of them useful :)

      • arhan

        Hi! Well, if you renamed the blog post to really describe the intent and/or added your very last paragraph into the blog post – that would clearly make things easier. I do understand the real intent of blogging for the content strategy of tech companies, and while 99% of the content is very nice, titles like this leave a cheesy aftertaste.

  • javawerks

    Yet another post that reflects poorly on Java developers, who clearly appear to be afraid of their own shadow since the release of Java 6. I remember a time when Java developers continually pushed the technical envelope — a rare phenomenon these days, practiced by few.

    Surely the author of this article, not unlike Pierre-yves ( who penned a most detailed and thoughtful critique ), didn’t think Java 8 would become a functional language like Clojure or Scala. Or did they? I suspect not. Yet, now, they both feel free to criticize Java 8, knowing full well in advance of its new and well-publicized features.

    This negative attitude, however technically correct at times, towards Java 8, has resulted in the best and brightest leaving the Java space. Moreover, just consider the millions of developers who never even considered developing in the Java space for cultural and technical reasons.

    Stating the obvious, clearly there is no nirvana-like language on the JVM. Many of you know I speak the truth. I could easily point out the many weaknesses of the top 10 JVM languages. Needless to say, it’s a frustrating state of affairs. Yet, at some point, we all must pick a JVM language, and move forward with our work. Alternatively, we can choose to leave the JVM space.

    For those of you who think Java is the one and only true JVM language, I would respectfully submit that by not migrating to Java 8 you are making a decision to limit, if not terminate, your future as a developer — emphasis on limiting your future in a COBOL kind of way by staying with Java 6 or 7.

    Naturally, this is just my considered opinion — which might very well turn out to be totally incorrect. Yet many of my observations, to date, none of which are original in thought, have already occurred.

    As they say — the quality of your life is often determined by the sum of the quality of your decisions. All the best, going forward, that is, if you choose to do so.:)

  • znogger

    Why not mention some more obvious ones:

    1. The new defaults in Javadoc (called doclint) will break your build. In particular if you are using Maven and some kind of CI (e.g. Hudson, Jenkins) you’ll quickly realize that you can’t just take your project and build in JDK8. Your build will almost certainly error out because of the new Javadoc. (I have yet to see a project that didn’t). The reason is that in Maven a return error code from the javadoc tool will stop the whole build … and as I said I have yet to see a project that would not somewhere have a Javadoc doclint noncompliance. Most larger projects (e.g. Netty) seem to solve the problem by simply turning off doclint (which of course wasn’t the idea behind doclint) or they stay on JDK7 for a while longer until they iron out all their comments problems.

    2. Code generation tools in JDK8 (e.g. wsimport) produce code that is not doclint compliant. This is a clear bug and well documented. It means that one part of the JDK8 is not working with another part of the JDK8. Expect to have to circumvent this somehow if you have a web consumer application.

    • http://www.takipi.com/ Alex Zhitnitsky

      Hi @znogger:disqus, thanks for the comment. Will check it out, didn’t look into doclint compliance.

  • Stephen Hammond

    This is c++ c# all over again. ~But I don’t have full control over how my array it iterated… wah~. For identifying places where Lambdas have benefits check this stackoverflow post (and realize it was written in 2009)…
    - http://stackoverflow.com/questions/672918/when-not-to-use-lambda-expressions

  • sannegrinovero

    None of these points make Java 6 or 7 better than 8, or 8 less suited as a runtime than any of its predecessors.

    If you don’t like some of these features, don’t use them but still where’s the point to “not switch” as promised in the title?