Simple question about java-8
syntax. Why does JLS-8
restrict such expressions like:
Object of_ref = Stream::of; // compile-time error
and allows only something like:
java.util.function.Function of_ref = Stream::of;
Object obj = of_ref; // compiles ok
?
Java 8 has introduced the idea of a Functional Interface, which allows you to essentially assign methods to variables. It includes a number of commonly-used interfaces as well.
Although all classes in Java are sub-classes of Object class, but different from other object types, a reference variable of type Object can't be assigned to any other reference type without a cast.
Yes, any lambda expression is an object in Java. It is an instance of a functional interface. We have assigned a lambda expression to any variable and pass it like any other object.
The method references can only be used to replace a single method of the lambda expression. A code is more clear and short if one uses a lambda expression rather than using an anonymous class and one can use method reference rather than using a single function lambda expression to achieve the same.
Object
is not a functional interface and a method reference can only be assigned to a functional interface. See for example JLS #15.13.2
A method reference expression is compatible in an assignment context, invocation context, or casting context with a target type T if T is a functional interface type (§9.8) and the expression is congruent with the function type of the ground target type derived from T.
That's because the target type of a method reference or a lambda expression should be a functional interface. Based on that only, runtime will create an instance of a class providing implementation of the given functional interface. Think of lambdas or method references as abstract
concept. Assigning it to a functional interface type gives it a concrete meaning.
Moreover, a particular lambda or method reference, can have multiple functional interfaces as its target type. For example, consider the following lamda:
int x = 5;
FunctionalInterface func = (x) -> System.out.println(x);
This lambda is a Consumer
of x
. In addition to that, any interface with a single abstract method with following signature:
public abstract void xxx(int value);
can be used as target type. So, which interface would you want runtime to implement, if you assign the lambda to Object
type? That is why you've to explicitly provide a functional interface as target type.
Now, once you got a functional interface reference holding an instance, you can assign it to any super reference (including Object
)
The key point is that there are no "function types" in Java. A lambda expression doesn't have a "type" by itself -- it can be typed to any functional interface whose sole method's signature matches the lambda. Therefore, a lambda's type is based on the type provided by its context. You must provide a functional interface as the context for it to get a type.
It is instructive to consider the same issue but for anonymous classes. Although there are implementation differences between lambdas and anonymous classes, semantically, lambdas are essentially equivalent to a subset of anonymous classes, and a lambda expression can always be converted to an equivalent anonymous class creation expression.
When you write:
Function<T, Stream<T>> of_ref = Stream::of;
it is equivalent to something like the following using anonymous classes:
Function<T, Stream<T>> of_ref = new Function<T, Stream<T>>() {
Stream<T> apply(T t) {
return Stream.of(t);
}
};
Now consider
Object of_ref = Stream::of;
what is the equivalent with anonymous classes?
Object of_ref = new [**What goes here?**]() {
[**What method signature goes here?**] {
return Stream.of(t);
}
};
You see why it doesn't make sense -- we don't know what type to use as the base class of the anonymous class.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With