Java 8 – Type Inference in Java 8

javajava8lambdatype-systems

Is the introduction of the new lambda notation (see e.g. this article) in Java 8 going to require some kind of type inference?

If so, how will the new type system impact the Java language as a whole?

Best Answer

There's a fair bit of incorrect information in ratchet freak's answer and in its comment thread. I'll respond here in an answer, since a comment is too small. Also, since this an answer after all, I'll attempt to answer the original question too. (Note however that I am not an expert on type systems.)

First, the short answers to the original question are Yes and No. Yes, Java 8 will have considerably more type inference than Java 7, and No, there is not a "new" type system in Java 8, although there are some minor changes.

Java 8 will still be statically typed, and it will still have the dichotomy between classes and interfaces. There are no new types such as function types. The type of a lambda is essentially a "functional interface" which is an ordinary interface with a single abstract method.

Interfaces can now have code in the form of default methods, but the model of single-inheritance of classes and multiple inheritance of interfaces remains the same. There are some adjustments, of course, such as rules for method resolution in the presence of default methods, but the fundamentals are unchanged.

Any type that's inferred by type inference could be written out explicitly. To use ratchet freak's example,

Collections.<MyClass>sort(list, (a, b) -> { return a.order - b.order; });

is basically sugar for

Collections.<MyClass>sort(list,
    (Comparator<MyClass>)((MyClass a, MyClass b) -> { return a.order - b.order; }));

So sparkleshy's statement "type inference doesn't require any extension of the type system" is basically correct.

But to return to syntactic sugar, I'll repeat my statement that a lambda expression is not syntactic sugar for an anonymous inner class. Ratchet freak stated that a lambda expression is translated into an anonymous inner class instantiation, and Sparkleshy simply reasserted that a lambda is syntactic sugar for an anonymous inner class, but these statements are incorrect. They are probably based on outdated information. Early lambda implementations did implement lambdas this way, but things have changed.

Lambda expressions are semantically different from inner classes, and they are implemented differently from inner classes.

Lambda expressions are semantically different from inner classes in a couple ways. Evaluating a lambda expression need not create a new instance each time. They also have different capture semantics, for example, they capture this differently. In an inner class, this is the inner class instance, whereas in a lambda, this is the enclosing instance. Consider the following:

public class CaptureThis {
    void a(Runnable r) { r.run(); }

    void b() {
        a(new Runnable() { public void run() { System.out.println(this); }});
        a(() -> System.out.println(this));
    }

    public String toString() { return "outer"; }

    public static void main(String[] args) { new CaptureThis().b(); }
}

In a recent JDK 8 lambda build (I used b69), the output will be something like the following:

CaptureThis$1@113de03
outer

Furthermore, lambda expressions are implemented completely differently from inner classes. If you compare the disassembled output, you'll see that the inner class code straightforwardly compiles to the creation and call to a constructor of CaptureThis$1, whereas the lambda expression compiles to an invokedynamic instruction that procures a Runnable through means unspecified. For a full explanation of how this works and why, see Brian Goetz' JavaOne 2012 talk Lambda: A Peek Under The Hood.