Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can I not assign a method reference directly to a variable of Object type?

Tags:

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

?

like image 518
Andremoniy Avatar asked Mar 16 '15 16:03

Andremoniy


People also ask

Can you assign a method to a variable?

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.

Can a reference variable of one type refer to the object of other type?

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.

Can a lambda expression be assigned to an object variable?

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.

How do you use method reference instead of lambda?

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.


3 Answers

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.

like image 198
assylias Avatar answered Sep 26 '22 20:09

assylias


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)

like image 8
Rohit Jain Avatar answered Sep 26 '22 20:09

Rohit Jain


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.

like image 8
newacct Avatar answered Sep 25 '22 20:09

newacct