Short History of Java
Java originated in 1995 and it was the language of the Internet and provided some unheard of capabilities in mainstream static typed languages, such as garbage collection, cross-platform out of the box (Write Once Run Anywhere), automatic documentation and the ability to run inside the browser as Java Applet. It quickly became very popular, especially for enterprise systems. About six years later, C# was released. The C# designers had taken a good look at Java and produced a language that was very similar, but addressed many of the issues that came to the front during Java's first years. Ever since, Java has been playing catch-up with C# and other languages in terms of language features.
JVM and Other Languages
One of Java's prominent features is its verbosity. It helps beginners, and experts, to understand large codebases. It was not a problem when the main alternative was C++, which is also verbose, but over the years, more dynamic functional languages with a much more light weight syntax gained traction — such as Python, Ruby, and in recent years Scala and Clojure. Developers flocked to these other languages, but wanted to take advantage of the Java ecosystem with its multitude of high-quality libraries and its fine-tuned cross-platform virtual machine. In the cases of Ruby and Python the results were JRuby and Jython, which were implementations of these languages designed to run on the JVM and integrate deeply with existing Java code. Scala and Clojure were designed from the get go to run on the JVM. So, the Java ecosystem flourished, but the Java programming language was left somewhat in the shadows as far as excitement goes.
The release of Java 8 was an effort to push the Java language once again to the center of the ring by offering various functional features in the core language. It will be very interesting to see how it plays out. In the meantime, let's explore some of those features.
Lambda expressions provide anonymous functions, which you could sort of get with anonymous classes, but the syntax was very cumbersome. There are many more features that Lambda expressions support, such as better interaction with functional interfaces, method references, streams and closures. I'll cover those in the next sections. But first, let's see Lambda expressions in action. The following code shows a Java 8 snippet that finds the first cool name in a list of names using a Lambda expression. In this case, a name is cool if it contains numbers or if it contains at least one instance of the letter K and no instances whatsoever of the letter C.
Here is a sample run:
> java Main playa kitCat KoolKat dog
A method reference are syntactic sugar for implementing a Lambda expression that calls an existing method. For example, suppose we already had a method for checking coolness, called (you guessed it) "checkCoolness". Here is what our program will look like with predicate defined using a reference to to checkCoolness() method:
Without method reference you would have to write:
CoolPredicate p = (s) -> Main.checkCoolness(s);
With method references it's just:
CoolPredicate p = Main::checkCoolness;
I, personally, don't see the big deal.
Streams add lazy evaluation to Java and take full advantages of Lambda expressions. It is conceptually very similar to the awesome .NET LINQ (Language integrated query). Here is an example, where a list of names is filtered for coolness and then the phrase " is a cool name" is added to each one:
If you run this code the output will be:
> java Main playa kitCat KoolKat dog Kawabanga
[KoolKat is a cool name, Kawabanga is a cool name]
Java 8 has limited support for closures, which means Lambda expressions can capture variables from their enclosing scope as long as those are effectively final (never modified). Here is an example:
As you can see, the Lambda expression uses the suffix variable declared in the enclosing scope.