A Lesser-Known Java 8 Feature: Generalized Target-Type Inference


Going through the list of Java 8 features, Generalized Target-Type Inference struck me as a particularly interesting, lesser-known gem. It looks as though the Java language designers will ease some of the pain that we’ve been having with generics in the past (Java 5-7). Let’s have a look at their example:

class List<E> {
  static <Z> List<Z> nil() {..}
  static <Z> List<Z> cons(Z head, List<Z> tail) {..}
  E head() {..}
}

Given the above example, the JEP 101 feature claims that it would be nice to be able to write:

// This:
List.cons(42, List.nil());
String s = List.nil().head();

// ... instead of this:
List.cons(42, List.<Integer>nil());
String s = List.<String>nil().head();

Being a fluent API designer myself, I was thrilled to see that such an improvement is on the roadmap, particularly the latter. What’s so exciting about these changes? Let me comment on that more in detail:

// In addition to inferring generic types from
// assignments
List<String> l = List.nil();

// ... it would be nice for the compiler to be able
// to infer types from method argument types
List.cons(42, List.nil());

// ... or from "subsequent" method calls
String s = List.nil().head();

So in the last example where methods are chained, the type inference would be delayed until the whole assignment expression has been evaluated. From the left-hand side of the assignment, the compiler could infer that <Z> binds to String on the head() call. This information could then be used again to infer that <Z> binds again to String on the nil() call.

Sounds like a lot of trickery to me, as the nil() call’s AST evaluations would need to be delayed until a “dependent” sub-AST is evaluated. Is that a good idea?

Yes, this is so awesome!

… you may think. Because a fluent API like jOOQ or the Streams API could be designed in a much much more fluent style, delaying type inference until the end of the call chain.

So I downloaded the latest evaluation distribution of the JDK 8 to test this with the following program:

public class InferenceTest {
    public static void main(String[] args) {
        List<String> ls = List.nil();
        List.cons(42, List.nil());
        String s = List.nil().head();
    }
}

I compiled this and I got:

C:\Users\Lukas\java8>javac InferenceTest.java
InferenceTest.java:5: error: incompatible types: 
    Object cannot be converted to String
        String s = List.nil().head();
                                  ^
1 error

So, the type inference based on the method argument type is implemented (and thus, compiles), but not the type inference for chained method calls. I searched the internet for an explanation and found this Stack Overflow question linking to this interesting thread on the lambda-dev mailing list.

It appears that the Java type system has become quite complex. Too complex to implement such crazy type inference stuff. But still, a slight improvement that will be greatly valued when writing every day Java 8 code.

And maybe, in Java 9, we’ll get val and var, like everyone else ;-)

Tags: , , , , , , ,

2 responses to “A Lesser-Known Java 8 Feature: Generalized Target-Type Inference”

  1. Martin Andersson says :

    If we ever get “val” or “var”, I will personally go out across the globe and uninstall Java on every computer I find.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 1,239 other followers

%d bloggers like this: