Use of type parameter in Java method reference

Zz'Rot :

In Java Precisely 3rd Ed., there's the following code snippet:

BiConsumer<Double[], Comparator<Double>> arraySorter = Arrays::<Double>sort;

However, I noticed that even when I leave out <Double> after ::, the method reference is still valid (which makes sense due to type parameters for BiConsumer).

However, I'm pretty confused about whether there are cases in which ::<T> would be necessary in a method reference, and if so, an example would be very helpful.

AJNeufeld :

I figured Java 10's local variable type inference (var name = ...;) would be the answer to this conundrum. Instead of the destination variable type providing the type for the method reference, the right-hand-side would need to fully specify the type, necessitating the type parameter (::<T>) on the method reference.

First thought out the gate ...

var arraySorter = Arrays::<Double>sort;

... but method references, by themselves, do not define a type. They need to be translated to a functional object by the compiler, and the compiler won't search for known functional interfaces looking for the appropriate type, even if there was exactly one.


Next thought was to use a method reference as an argument to a method which returns a type based on the method's argument.

class Spy {
    static <T> Function<T,T> f2(Function<T,T> f) {
        return f.andThen(f);
    }

    static <T> T identity(T t) {
        return t;
    }
}

Using this, we can create our local variable passing a method reference to our method:

Function<Double,Double> double_identity = f2(Spy::<Double>identity);

As expected, we can remove the ::<Double>

Function<Double,Double> double_identity = f2(Spy::identity);

Unexpectedly, local variable type inference is fine with it.

var double_identity = f2(Spy::identity);             // Infers <Object>!
Object obj = null;
double_identity.apply(obj); 

But the real surprise comes when use the method reference type to override it.

var double_identity = f2(Spy::<Double>identity);     // Error: Double != Object

After a bit of fighting, I figured out why. We have to apply the type to the f2 method itself:

var double_identity = Spy.<Double>f2(Spy::identity); // Works.

In retrospect, this makes some sense. The type of the variable generally provides context for the outer function. Assigning the result to a Function<Double,Double> variable lets the compiler infer the type of f2(...), which then passes that type to the arguments. With var name = ..., without an explicit type, the only type is has available is Object, so the compiler infers Spy.<Object>f2(...), and then determines the argument type must be a Function<Object,Object>.

Unfortunately, it doesn't seem to parse from the inside out, so that Spy::<Double>identity doesn't cause the function to be inferred as Spy.<Double>f2(...) and the variable as Function<Double,Double>. Maybe Java 11? Maybe it would break too much, and can't work.

It does, however, put an end to my attempts to abuse var name = ...; to solve the OP's conundrum.


Many thanks to @Eugene for critiquing my previous attempts prior to Java 10's release.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=474386&siteId=1