Java 8 Streams map API - interpretation of method reference

一笑奈何 提交于 2019-12-10 10:16:49

问题


Sample code:

class Outer {
    public Integer i;

    Outer(Integer i) {
        this.i = i;
    }

    public int getVal() { return i; }
}

class MyClass {

    public Integer f(Outer o) { return o.getVal();};

    public void main() {

        MyClass g = new MyClass();

        List<Integer> l1 = Arrays.asList(new Outer(2)).stream().map(g::f).collect(Collectors.toList());
        List<Integer> l2 = Arrays.asList(new Outer(2)).stream().map(Outer::getVal).collect(Collectors.toList());
    }
}

Using either of the method references of

  1. Outer::instanceMethod that takes no argument and is basically a Supplier<T> functional interface. [1]

  2. MyClass::instanceMethod that takes an argument of type Outer and is a Function<T,R>functional interface. [1]

is valid. Then how does the map function know to apply the function in option (1) to the objects of the stream, but pass the stream objects to function in option (2)?

[1] https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html


回答1:


First of all the map method does not know itself what to do with method references. That's the compiler's job. In both cases, map expects :

Function<? super PackageName.outer,? extends Integer>

For you particular question both method references according to the docs are a Reference to an instance method of a particular object

Regarding how the compiler deals with lambdas and method references and translates them to bytecode this document is highly recommended reading. The most relevant part to your question (emphasis mine to summarise):

When the compiler encounters a lambda expression, it first lowers (desugars) the lambda body into a method whose argument list and return type match that of the lambda expression, possibly with some additional arguments (for values captured from the lexical scope, if any.) At the point at which the lambda expression would be captured, it generates an invokedynamic call site, which, when invoked, returns an instance of the functional interface to which the lambda is being converted. This call site is called the lambda factory for a given lambda. The dynamic arguments to the lambda factory are the values captured from the lexical scope. The bootstrap method of the lambda factory is a standardized method in the Java language runtime library, called the lambda metafactory. The static bootstrap arguments capture information known about the lambda at compile time (the functional interface to which it will be converted, a method handle for the desugared lambda body, information about whether the SAM type is serializable, etc.)

Method references are treated the same way as lambda expressions, except that most method references do not need to be desugared into a new method; we can simply load a constant method handle for the referenced method and pass that to the metafactory

Instance-capturing method reference forms include bound instance method references (s::length, captured with reference kind invokeVirtual)

The bytecode for your 2 cases is :

  1. outer::instanceMethod

    // handle kind 0x5 : INVOKEVIRTUAL
    PackageName/outer.getVal()I, 
    (LPackageName/outer;)Ljava/lang/Integer;
    
  2. MyClass::instanceMethod

    // handle kind 0x5 : INVOKEVIRTUAL
    PackageName/MyClass.f(LPackageName/outer;)Ljava/lang/Integer;, 
    (LPackageName/outer;)Ljava/lang/Integer;
    

Note that, although the second line is more complicated in the second case, the last line is the same. In both cases the compiler just sees a function that takes an outer and returns an Integer. And that matches what map expects.

Method References are described in the language spec 15.13 Method Reference Expressions. The fact that the target reference of a method reference is an implicit first argument of the method is mentioned at 15.13.3 Run-Time Evaluation of Method References.

If the compile-time declaration is an instance method, then the target reference is the first formal parameter of the invocation method. Otherwise, there is no target reference



来源:https://stackoverflow.com/questions/41532611/java-8-streams-map-api-interpretation-of-method-reference

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!