Java 8 Streams map API - 方法参考的解释 [英] Java 8 Streams map API - interpretation of method reference

查看:118
本文介绍了Java 8 Streams map API - 方法参考的解释的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

示例代码:

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());
    }
}

使用


  1. Outer :: instanceMethod 不带参数,基本上是供应商< T> 功能界面。 [1]

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

MyClass :: instanceMethod 带参数键入外部并且是功能< T,R> 功能界面。 [1]

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

有效。那么 map 函数如何知道将选项(1)中的函数应用于流的对象,但是将流对象传递给选项(2)中的函数?

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

推荐答案

首先, map 方法不知道自己如何处理方法引用。这是编译器的工作。在这两种情况下, map 预计:

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>

根据 docs 对特定对象的实例方法的引用

关于编译器如何处理lambdas和方法引用并将它们转换为字节码本文档强烈推荐阅读。问题中最相关的部分(强调我的总结):

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):


当编译器遇到lambda表达式时 ,它首先将
(desugars)lambda体降低为一个方法,其参数列表和
返回类型匹配lambda表达式,可能还有一些
的附加参数(对于从词法中捕获的值)范围,如果
any。)在捕获lambda表达式的时候,
它会生成一个invokedynamic调用站点,当调用它时,返回
的一个实例lambda被转换为
的功能接口
。对于给定的
lambda,此调用站点称为lambda factory 。 lambda工厂的动态参数是从词法范围中捕获的值
lambda
工厂的bootstrap方法
是Java语言运行时库中的标准化方法,
称为lambda metafactory 。静态引导参数在编译时捕获已知有关lambda的
信息(将转换为它的函数
接口,
desugared lambda体的方法句柄,有关SAM是否有关的信息类型是
serializable等。)

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.)

方法引用的处理方式与lambda表达式相同,
除外大多数方法引用不需要被置于
新方法中;我们可以简单地加载
引用方法的常量方法句柄并将其传递给metafactory

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

实例捕获方法引用表单包含绑定实例
方法引用(s :: length,使用引用类型
invokeVirtual
捕获)

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

您的2个案例的字节码是:

The bytecode for your 2 cases is :


  1. outer :: instanceMethod

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


  • MyClass :: instanceMethod

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


  • 请注意,虽然第二行更多在第二种情况下复杂,最后一行是相同的。在这两种情况下,编译器只看到一个函数,它接受 outer 并返回 Integer 。并且匹配 map 期望的内容。

    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.

    方法引用以语言规范 15.13方法参考表达式。在 15.13.3方法参考的运行时评估

    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

    这篇关于Java 8 Streams map API - 方法参考的解释的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆