31

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?

Stuart Marks
  • 2,225
  • 2
  • 19
  • 22
Giorgio
  • 19,486
  • 16
  • 84
  • 135

1 Answers1

49

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.

Stuart Marks
  • 2,225
  • 2
  • 19
  • 22
  • 2
    "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:" still can be done with syntactic sugar by replacing all `this` with `MyClass.this` – ratchet freak Jan 07 '13 at 02:38
  • 4
    Everything that goes beyond assembler (and sometimes even that) is arguably syntactic sugar. But lambdas are **not** syntactic sugar for inner classes (any more). They serve a similar goal and have some similarities, but under the hood they are (observably) different. – Joachim Sauer Jan 07 '13 at 08:29
  • 1
    "Lambda expressions are semantically different from inner classes, and they are implemented differently from inner classes.": Thanks for the very good explanation (+1). What is still not clear to me is **why** have lambdas not been implemented as syntactic sugar for special anonymous classes or, in other words, what is the advantage of the extra complexity introduced by the new semantics? What problem does this solve that cannot be solved with anonymous inner classes? (But I still have to look at the link you posted, maybe I will find the answer there.) – Giorgio Jan 07 '13 at 10:26
  • 1
    @Joachim Sauer: I would consider syntactic sugar a new syntax for an existing semantics. When new semantics is introduced, I think you cannot speak about syntactic sugar. In this sense, I do not think that you can argue that everything beyond assembler is syntactic sugar. – Giorgio Jan 07 '13 at 10:27
  • @Giorgio: no turing complete language can do stuff that another turing complete language can't do. They are equivalent on a very basic level, that's why I say "everything" is syntactic sugar. Of course, some concepts might be hard to implement in language A and trivial in B. – Joachim Sauer Jan 07 '13 at 10:31
  • "So sparkleshy's statement "type inference doesn't require any extension of the type system" is basically correct.": So the types of variables `a` and `b` in your example are inferred from the type of the interface `Collections.sort()`? – Giorgio Jan 07 '13 at 10:36
  • @Joachim Sauer: I think there is a difference. Suppose that a language has a multiplication operation that takes an arbitrary number of arguments and is written `*[2, 3, 8, ...]`. Now, when you have exactly two parameters, you can introduce the syntactic sugar (alternate syntax) `2 * 3`, which is just another way of writing `*[2, 3]`. In this case, no new concept is being added to the language (the semantics is unchanged). This is different from introducing a binary multiplication with syntax `2 * 3` in a language that **did not** have multiplication at all. – Giorgio Jan 07 '13 at 10:49
  • 3
    @Giorgio: regarding why lambda isn't just syntactic sugar for anonymous inner classes, turns out there was a large discussion about this. This mail from Brian Goetz summarizes the decision: http://mail.openjdk.java.net/pipermail/lambda-dev/2011-August/003877.html . TL;DR it leaves the door open for future evolution and we get better performance today. Goetz is actually answering a related question, Are lambdas objects? But if the answer is No or even Maybe that implies they cannot be sugar for inner classes. – Stuart Marks Jan 08 '13 at 01:22
  • @Giorgio: re inference, actually it's Ratchet Freak's example. It calls `Collections.sort` but in fact this isn't necessary. One could call `Collections.sort(list, ...)` and this would work, assuming that `list` is of type `List`. Thus, the type arg `T` must be `MyClass` and the second arg must be of type `Comparator super MyClass>`. This much inference was in 7. In 8, the second arg's type is now considered to be the **target type** that is used in inferring the type of the lambda expression. The `compare` method is the only abstract method on `Comparator`. (continued...) – Stuart Marks Jan 08 '13 at 01:32
  • (... continued) Given that the signature of `compare` is `int compare(T o1, T o2)` and `T` is `MyClass` it follows that `a` and `b` are of type `MyClass`. Actually it's a bit more complicated since the target type parameter is `? super MyClass`. I'm not sure how we get from here to just `MyClass`. The inference algorithm might just take the bound of the wildcard. Finally, it then checks that the expression `a.order - b.order` works with `MyClass` and the target return type of `int`. It checks out, so we're done. All this is reasoning mostly within the current type system. – Stuart Marks Jan 08 '13 at 01:39
  • @Stuart Marks: Thanks a lot for the explanation and the very useful link. – Giorgio Jan 08 '13 at 10:20
  • @StuartMarks: your explanation was very interesting to me as I'm trying to understand how java lambdas map to .NET ones. It appears, that .NET's concept of "delegates" was translated in Java into these functional interfaces... An interface with one single default method, this also sounds like a kind of OO function pointer. All of this makes sense. Add a pinch of type inference (all that's missing to the picture being the var keyword) and you've got a pretty close equivalence between C# and java on the lambdas field. I suppose Java 8 collections handling will be very alike .NET's Linq... – odalet Mar 07 '14 at 23:50
  • 1
    I don't know enough about .NET to say whether its delegates are similar to Java's functional interfaces. There are some similarities with Linq, but again I don't know enough about Linq to comment on the details. Functional interfaces have a single **abstract** method (they can have many default methods) and yes, a reference to an object that implements a functional interface is kind of like an O-O function pointer. (This is sometimes called a "functor".) – Stuart Marks Mar 08 '14 at 05:06