Love and hate for Java 8

Java 8 brings exciting developments, but not without some headaches

Thanks to Michael Brush who contributed significantly to this article. -- ACO

Java 8 may be the most anticipated version of Java ever. Originally slated for release in September, Java 8 has been delayed until March of next year, supposedly to buy time to make security fixes aimed mainly at client-side Java (JavaFX/Swing). Since I, like most of you, stopped caring about client-side Java shortly after Duke finally finished jumping rope, we won't address any of that.

Java 8 is trying to "innovate," according to the Microsoft meaning of the word. This means stealing a lot of things that have typically been handled by other frameworks and languages, then incorporating them into the language or runtime (aka standardization). Ahead of the next release, the Java community is talking about Project Lambda, streams, functional interfaces, and all sorts of other goodies. So let's dive into what's great -- and what we can hate.

Streams

The changes to collections are driving a lot of the other changes in the language. The main component of making those collections better is a new collection called a "stream." This is not to be confused with java.io package's InputStream and OutputStream -- it's a separate concept altogether.

Streams are not meant to replace ArrayLists or other collections. They are only meant to make manipulating the data easier and faster. A stream is a one-time-use Object. Once it has been traversed, it cannot be traversed again.

Streams have the ability to filter, map, and reduce while being traversed. There are two "modes" for a stream: sequential and parallel. This is where the ability of the stream to use the multicore processors of today comes into play. It uses fork/join parallelism to split up the work and speed the processing along.

Using a sequential stream:

List <Person> people = list.getStream.collect(Collectors.toList());

Using a parallel stream:

List <Person> people = list.getStream.parallel().collect(Collectors.toList());

When the stream is traversed sequentially, each item in the stream is read processed, then the next item is read. When the stream is traversed in parallel, the array is split into multiple segments, each of which is processed individually on a different thread. The results are then put back together for the output.

Parallel stream flow:

List originalList = someData;

split1 = originalList(0, mid);

split2 = originalList(mid,end);

new Runnable(split1.process());

new Runnable(split2.process());

List revisedList = split1 + split2;

This very simplified example shows how the parallel Stream would be processed. This is how it will take advantage of multicore processors.

Since a Stream can only be traversed once and generally returns another Stream, use a terminal method to get a useful result. Examples of a terminal method are sum(), collect(), or toArray(). Until the Stream is terminated, the results of the operations are not realized. For example:

Double result = list.getStream().mapToDouble(f -> f.getAmount()).sum();

List<Person> people = list.getStream().filter(f -> f.getAge() > 21).collect(Collectors.toList());

The big benefit of this feature is the ability to use multiple processor cores for collection processing. Instead of doing the traditional for loop, use Stream in parallel mode -- theoretically, the speed goes up with each core added. The main problem that could arise from this is readability. With all of the chaining of streams, the lines could get long, which will affect readability. The other problems stem from the things that are built to support this new path. Those are functional Interfaces and Lambda.

Functional interfaces

Java 8 will have a new feature called functional interfaces. Basically, default methods are added to an interface and do not have to be overridden in the interface implementation. These methods can be run directly from the interface.

This was done for backward compatibility for your collections in your interfaces. It is to solve the problem of allowing the Stream to be put into an interface without having to change all of the classes to implement the new method. Basically, create a default method in the interface, and all the classes that implement the interface can use the Stream (or whatever is put into the default method). If the default method is not correct for the implementation, it can be overridden in the implementer.

What this essentially does is allow a form of multiple inheritance. This becomes the implementer's problem, as the implementer will be required to override the method anyway. The implementer could then choose which supermethod to use, but this means a lot of classes that implement interfaces could still be changed.

This is probably the detail that will concern most people in Java 8. Perhaps it won't bother those already familiar with Scala. It can be compared directly with the concept of traits in Scala; although the concept is not unique to Scala, it is best known to the Java world from Scala. However, there are some differences: Java 8 functional interfaces can not get a reference to the implementing class. Scala allows this with the self keyword. Language nerds will say that Java 8's functional interfaces allow multiple inheritance of behavior, but not state. Scala's traits are multiple inheritance for both behavior and state.

Consider the power of traits and the stuff we do to work around not having them in Java. In Java to implement transactions and other items, we construct dynamic proxies and do bytecode manipulation with JavaAssist or extend classes with cglib. Traits give us ways to do this more directly in some cases.

On one hand, functional interfaces will probably be misused the way that inheritance is misused. On the other hand, they don't go as far as Scala's traits. We'll still be stuck with some level of bit-twiddling class-loader implementation via Aspect Oriented Programming-type notes in Java in places where traits could probably stand in.

Lambda

Lambda expressions are coming to Java in version 8. Lambda expressions are designed to allow code to be streamlined. When a Lambda expression is written, it is translated into a functional interface at compile time. Here is an example of using Lambda expressions to replace an anonymous inner class with much cleaner and more readable code.

Old way without Lambda:

button.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent ae) {

System.out.println(“Action Detected”);

}

}

);

New way with Lambda:

button.addActionListener(e -> {

System.out.println(“Action Detected”);

}

);

Let's look at one more quick example.

Old way without Lambda:

Runnable runnable1 = new Runnable() {

@Override

public void run() {

System.out.println("Running without Lambda");

}

};

New way with Lambda

Runnable runnable2 = () -> { System.out.println("Running from Lambda"); };

As you can see, using Lambda expressions will often make the code easier to read and require fewer lines. This addition has excited a lot of people around the Java community. With Scala, however, we have a somewhat widely used JVM language with all of these features already in it.

Not surprising, the Scala community is incredulous because many of Java 8's additions merely look like Scala replacing the => operator with ->. In some cases, Java 8's syntax has been pointed out to be more verbose or less clear than Scala's. It's yet to be determined what, if anything, it will do to languages like Scala that are built around Lambda expressions.

On the one hand, if Java continues to evolve and implement everything around Lambda as Scala has done, then there may be no need for Scala. On the other hand, if it is left alone to provide only core functionality, such as helping with anonymous inner classes, then Scala and other languages will continue to thrive and ride on top of Java as they do now. This is the best outcome; it will allow other languages to continue to grow and be inventive without worrying about whether or not they will become obsolete.

Java time

Java has had a long history with time, pun intended. First there was the java.util.Date class, which quickly showed us that Sun could deprecate methods in record time, but that deprecated methods might be eternal. Oh, don't forget java.sql.Date, which helped us realize the time and place for using fully qualified names (FQNs) in code.

Next, there was the Calendar, which realized that code might have to deal with more than one part of the world in the same JVM. But dealing with times and dates required a lot of monkey code and fiddling with a fractured Java API. From this was born third-party libraries such as JodaTime among others. Now Java has decided belatedly to clean it up with a java.time package. This looks to me like everything we always wanted the various temporal APIs to be.

While it is certainly to be applauded, the Java API could use some cleanup and depth on other items in the spacetime continuum: distances, mass, weights and more. I also think Currency could be handled better. While I generally believe the Java API needs a good pruning rather than more items, I have to say this basic element of nature probably should have the kind of standard compatibility the Java API is intended to provide.

Nashorn

Netscape created a piece of software called LiveScript to allow for scripting on its Web servers. It decided to port it to its browser and needed a fancier name, so it licensed the Java trademark from Sun and called it JavaScript -- which would long promote the confusion that JavaScript had very much to do with Java. However, after the apAOLcalypse, some members of the 12 colonies of Netscape were not done and sought to continue Netscape's plan of rewriting their browser in Java. In order to do so, it needed to create an implementation of JavaScript in Java. Netscape called the project Rhino; as with turducken, ours is not to question but to enjoy.

All joking aside, modern JavaScript isn't your father's JavaScript (yes, you theoretically could've learned JavaScript as a first language, then have an 18-year-old who's learning the newer dialects of it). It can be useful on both the server and the client, and you can write applications in it that are neither slow nor unreadable.

JDK 7 added support for dynamic languages to not suck on the JVM and called it invokeDynamic. JDK 8 will give us a more useful JavaScript implementation and possibly make Nodyn (Red Hat's port of Node.js to the JVM) -- which I call turduckaturducken -- not a kooky engineering boondoggle. In fact, the masters of open source and collaboration that they are, Oracle has its own Node.js implementation creatively called Node.jar. This seems to have caught more people's imagination than JavaScript from Java. If you believe the buzz, what most people are sure about is that they want to run stuff on the JVM, but they don't want that stuff to be written in the crickety ol' Java syntax.

While there are many places that it can be useful to run JavaScript from within Java (for instance, it is one way of using the same client-side validator as server-side validator), having your Node.js and Java too is like having your turducken cake and eating it too. Who wouldn't want that tasty beast? If in reading this you are unsure whether I'm being serious or sarcastic, that makes two of us.

Accumulators

I generally am not a fanboy and would rather make fanboys (particularly of the Apple ilk) cry. However, there are a few exceptions. One day I want to have Doug Lea sign my chest with a black Sharpie and, like a true fan, not shower for at least a week. I'm just saying that man can code. Doug Lea is the architect of modern concurrency in Java. Since java.util.concurrent was integrated, time has not stood still. JDK 8 will build on the fork/join framework added to JDK 7 and go further with adders and accumulators.

First there was synchronized. However, if all you need to do is increment a count across many threads, then synchronized is a bit heavy. It became less heavy in Java 6 by making uncontended locks cheaper. Mostly that helped old applications that still used Vector, and that almost single-threaded pile of crap that has infected every library known as the Java Activation Framework.

Package java.util.concurrent made everything better for thread pools and other relatively complex multithreaded constructs, but if all you want to do is increment a variable across threads, it was overkill and then some. For this we were given atomics, far faster and lighter than real locks. However, Doug Lea and his minion army of grad student slave labor are not done yet. In JDK 8 we will be given accumulators and adders. These are lighter weight than atomics with lower guarantee, though they're generally good enough for what most concurrent code needs for having a few threads that must increment a count. Expect to see it for things like map/reduce implementations or anywhere you need to sum a value across threads. You'll still need atomics if you have to read that value across those threads as the order of accumulation isn't guaranteed.

HashMap

fixes

There is currently a known bug with the way String.hashCode() is implemented in Java. If a specially crafted set of HashMap are introduced, a denial-of-service attack could result in the code. Basically, if enough of the parameters added hash to the same value, it will cause excessive CPU time.

Currently, the HashMap bucket uses a linked list to store the map entries. Using this algorithm, if there are a high number of collisions, then the complexity of the hash changes from O(1) to O(N). To resolve this, once the number of items in a bucket reaches a certain threshold, the bucket will switch to using a balanced tree algorithm in order to reduce the complexity to O(log n). Hopefully it will address denial-of-service bugs like this one that Oracle says is not its problem.

TLS SNI

SNI isn't the name of a Dr. Seuss character. It is Server Name Identification. So everyone loves SSL or TLS or whatever its calling itself these days. However, while we are running out of IPv4 addresses, no one except for neckbeards and cat ladies actually wants IPv6.

Since most public sites don't have thousands, let alone millions, of users (whoo-hoo, hi, to the 10 to 12 people who read my personal blog), a lot of sites use the same IP address and a name-based virtual host. What that means is the second line of the HTTP request is the hostname. I can ask for podcasts.infoworld.com and www.infoworld.com, ending up at the same IP address, but based upon the hostname, I might get two different Web pages. However, many times I can't share IP addresses because of SSL. Due to the HTTP Host header on which name-based virtual hosts rely on comes after SSL encryption/decryption, I have to have a different IP for every SSL certificate.

This was addressed in the last few years with something called SNI; now Java has it too. Most recent browsers support it, Apache supports it, and Java now supports it. This means that before long we can expect Tomcat and other Java-based servers that use Oracle's slow-motion SSL implementation known as JSSE to support SNI.

All in all, some good features are coming to Java 8, but there are catches. Some of it will be in the way that developers choose to use the new technologies. The streams are by far the best new feature in my opinion. Hopefully the parallelism of collections will increase their processing performance. I can see uses similar to cache spaces. Bring your dataset into memory, and when you need specific things out of it, run it through a parallel stream to get at the data you need. The feature that is going to be dangerous is functional interfaces. If they are not used properly, they could cause a lot of headaches.

This article, "Love and hate for Java 8," was originally published at InfoWorld.com. Keep up on the latest developments in application development and Java, and read more of Andrew Oliver's Strategic Developer blog at InfoWorld.com. For the latest business technology news, follow InfoWorld.com on Twitter.

This story, "Love and hate for Java 8" was originally published by InfoWorld.

Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more