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

后端 未结 5 678
陌清茗
陌清茗 2020-12-09 09:43

Simple question about java-8 syntax. Why does JLS-8 restrict such expressions like:

Object of_ref = Stream::of;  // compile-time er         


        
相关标签:
5条回答
  • 2020-12-09 09:52

    You can! You just need to give the compiler a little more information so it knows what functional interface the method reference should implement:

    Object obj = (Function<?, ?>) Stream::of;
    

    Method references and lambda expressions work by using type inference to determine what interface the anonymous class they create should implement. Without the Function cast, the only type Java has to work with is Object - which most certainly isn't a functional interface (an interface with only one non-static non-default method). Explicitly casting the method reference expression to a Function provides the missing type information we need, then we can assign the Object field to the function because Function is a subtype of Object.

    0 讨论(0)
  • 2020-12-09 09:53

    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.

    0 讨论(0)
  • 2020-12-09 09:55

    I suspect this is a purely academical question, since I can't see any real-life use case for this. Nevertheless, I am pretty sure that it has to do with Stream::of being a lambda expression. You could also not do this:

    Object of_ref = list -> Stream.of(list);
    

    I speculate that an accurate return type tells the compiler which FunctionalInterface it being used. Without this information it is impossible for the compiler to resolve the Lambda expression correctly and unambiguously.

    0 讨论(0)
  • 2020-12-09 10:01

    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)

    0 讨论(0)
  • 2020-12-09 10:14

    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.

    0 讨论(0)
提交回复
热议问题