Java 8, or Oracle finally catches up to .NET Framework 3.0

Java LogoSo Oracle is finally releasing Java 8... sometime.  Since my work is pretty aggressive about upgrading Java versions, I might just get a chance to use it, and I'm actually pretty psyched because there are some cool new features.

In this post, I'm going to cover the new stuff in both Java 7 and 8 that you'll actually want to use on a regular basis.

New in Java 7

Strings in Switch Statements

This is a no-brainer, and has been a feature in C# since the beginning.  The literals in switch statements can now be strings.  If your string matches the literal, its corresponding case statement is executed:

switch (ordinal.toLowerCase())
{
case "first":
   return 1;
case "second":
   return 2;
case "third":
   return 3;
}

Try-With-Resources

This is equivalent to the using keyword in C#.  Any object that implements the new java.lang.AutoCloseable interface (equivalent to .NET's IDisposable) can be used in this way.  The syntax is identical to the C# except that the keyword try is used, and you can tack on a catch or finally if you like.

try (InputStream is = new FileInputStream(path))
{ 
   // do some stuff with the stream
} // the stream is automatically closed when execution leaves this block

New in Java 8

Lambda Expressions

Lambda expressions in Java 8 are identical to C# lambdas down to the syntax, except that Java uses "->" where C# uses "=>" to separate the arguments from the function body.  As in C#, the syntax is flexible; all of the following are equivalent:

// take a string and return a copy with '_1' appended
str -> str + "_1";
(str) -> str + "_1";
(String str) -> str + "_1";
str -> { return str + "_1"; }
// et cetera

The under-the-hood difference in Java is that lambdas are not things that can be created on their own; a lambda takes the place of an interface with a single method, like Runnable.  The compiler then generates an anonymous class with your code in that method.  So for example, these are equivalent:

Thread t = new Thread( () -> { /* do some stuff */ } );
Thread t = new Thread( new Runnable()
   {
      public void run() { /* do some stuff */ }
   } );

You can also pass a method when you could use a lambda, just as with C# delegates. Unlike C#, the compiler is not passing a reference to the method; it's instead creating an anonymous class whose single member function calls the method.  These are therefore also equivalent:

Thread t = new Thread( System.out.println );
Thread t = new Thread( () -> System.out.println() );
Thread t = new Thread( new Runnable()
   {
      public void run() { System.out.println(); }
   } );

So while lambdas are nice syntactic sugar, they're still not quite as flexible as C# delegates.

Default ("Defender") Methods

Neither Java nor C# allow multiple inheritance but both allow a class to implement any number of interfaces.  Interfaces are great, but what happens when you want to add methods to existing interfaces like Java's Collection<T> or .NET's IEnumerable<T>?  In C#, this was a critical obstacle when LINQ was added; IEnumerable<T> needed a bunch of new methods but modifying the interface would break every user-defined class that implemented it!

C# fixed the problem with extension methods, which allow anyone to create static methods that can be used in code as if they were member functions of a different class or interface.

Java fixes this problem by allowing interface methods to have default implementations (sometimes called "defenders"), which can be overridden by derived classes but don't have to be.  Default interface methods can therefore do most of what extension methods can, except that they're confined to interface classes rather than being available to all types, and programmers cannot add default methods to interfaces that are already part of the language or a third-party library.

Adding a defender to an interface is easy; declare the method with the default keyword and implement it in the interface class declaration:

public interface Foo
{
   // normal interface method (must be overridden in implementing classes)
   public void bar();
   // default interface method (can be overridden, does not have to be)
   default public int baz() { return 1; }
}

Streams

Java streams (not to be confused with InputStream and OutputStream) do more or less the same thing as LINQ, with parallel processing mirroring PLINQ.  There isn't any nice SQL-like syntax to use, though - you have to do it function-style.  And just as LINQ required extension methods, streams don't appear until Java 8 because they need defender methods to work with existing collection types.

Stream<T> is largely equivalent to .NET IEnumerable<T>.  To see how they're similar, consider these examples:

// Write each value in a collection to standard output on a separate line:

// C# - LINQ
myCollection.ForEach( x => Console.WriteLine(x) );
// Java - stream
myCollection.stream().forEach( x -> System.out.println(x) );

// Sum all the values in a (potentially large) collection, using parallelism
// if possible:

// C# - PLINQ
int sum = myCollection.AsParallel().Aggregate( (x, y) => x + y );
// Java - parallel stream
int sum = myCollection.stream().parallel().reduce( (x, y) -> x + y );

You would expect the stream() method to be on Iterable<T>, in the same way as LINQ operates on IEnumerable<T>, but it's on Collection<T> instead.  Perhaps it's because Java lacks yield-return semantics, so Iterable<T> is just less interesting or useful in Java.

Conclusion

With Java 8, you can now use nearly all of the syntactic sugar you love from C# in Java.  Still remaining to be added: type inference (like the C++ auto and C# var keywords) and basic functional programming syntax (like SQL-style LINQ in C# and list comprehensions in Python).  But I'm sure they'll be there by the time Java 10 or 11 is released, sometime in 2020.

6 thoughts on “Java 8, or Oracle finally catches up to .NET Framework 3.0”

  1. Now it's just a matter of time until somebody has a strong opinion about why the latest version of our product isn't built on Java 8, disregarding that it was released months before Java 8 was. Just like Java 7. And most Java 6 updates, actually.

    1. The only people who should really care what your code is written in are the people who write said code. I realize that won't actually stop people from complaining, but...

    2. HA! Are you kidding?? -- Missing properties -- missing extension methods -- generics in Java are a joke (Contravariant/Covariant still lacking) -- the JRE's garbage collector and entire memory model are from the stone-age compared to .NET -- no LINQ as already mentioned, no actual yield return still (this is fucking huge guys), man, this is just too damn long. Writing code in Java is fucking painful. If you have to target the JRE switch to Scala at least ... and maybe pickup a religion so you can pray to the gods for a merciful end to your suffering while you're at it.

      1. Sorry it took so long to approve your comment - I didn't get a mail notification (gotta fix that).
        Java ain't so bad, and it's still the language you can program in and assume what you did will run pretty much anywhere.
        It's also fairly standard for desktop application plug-in infrastructures.

        So from a practical standpoint, it's not nearly as bad of a choice as you seem to think.
        And, being a Turing-complete language, even without the syntactic sugar we love, you can still simulate yield return in a number of ways if you need to.

  2. excellent post!
    i dont understand why java is so sluggish in terms of lang features.
    this really sucks because we do cross lang dev using Unistack approach,
    we target .net and java runtimes only not using any library bloat, only clr/bcl and jvm, so we constantly struggle on java side because there are no properties, yield return, lambdas (until 8). we also looked at Scala by decided against it because it is very easy to abuse it to a point of nonusability (at least regular devs get lost in it)

Leave a Reply

Your email address will not be published. Required fields are marked *