在Java8中引用具有不同参数的方法 [英] Reference to methods with different parameters in Java8

查看:161
本文介绍了在Java8中引用具有不同参数的方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道所有这些方法参考和功能接口如何在较低级别上工作。
最简单的例子是我们有一些List

I'm wondering how does all this stuff with method references and functional interfaces works on lower level. The easiest example is where we have some List

List<String> list = new ArrayList<>();
list.add("b");
list.add("a");
list.add("c"):

现在我们想用收藏夹对它进行排序class,所以我们可以调用:

Now we want to sort it with Collections class, so we can call:

Collections.sort(list, String::compareToIgnoreCase);

但是如果我们定义自定义比较器,它可能是这样的:

But if we define custom comparator it could be something like:

Comparator<String> customComp = new MyCustomOrderComparator<>();
Collections.sort(list, customComp::compare);

问题是Collections.sort有两个参数:List和Comparator。由于Comparator是功能接口,因此可以使用具有相同签名(参数和返回类型)的lambda表达式或方法引用替换它。那么它如何工作我们可以传递也引用compareTo只接受一个参数并且这些方法的签名不匹配?
如何在Java8中翻译方法引用?

The problem is that Collections.sort takes two parameters: List and Comparator. Since Comparator is functional interface it can be replaced with lambda expression or method reference with the same signature (parameters and return type). So how it works that we can pass also reference to compareTo which takes only one parameter and the signatures of these methods don't match? How are the method references translated in Java8?

推荐答案

来自 Oracle方法参考教程


对特定类型的任意对象的实例方法的引用

以下是参考的示例到特定类型的任意对象的实例方法:

The following is an example of a reference to an instance method of an arbitrary object of a particular type:

String [] stringArray = {Barbara,James,Mary ,John,
Patricia,Robert,Michael,Linda};


数组。 sort(stringArray,String :: compareToIgnoreCase);


方法引用的等效lambda表达式 String :: compareToIgnoreCase 将具有形式参数列表(String a,String b),其中a和b是用于的任意名称更好地描述这个例子。方法引用将调用方法 a.compareToIgnoreCase(b)

String[] stringArray = { "Barbara", "James", "Mary", "John", "Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);

The equivalent lambda expression for the method reference String::compareToIgnoreCase would have the formal parameter list (String a, String b), where a and b are arbitrary names used to better describe this example. The method reference would invoke the method a.compareToIgnoreCase(b).

但是, :: 运算符到底意味着什么呢?好吧, :: 运算符可以这样描述(来自这个问题):

But, what does the :: operator really mean? Well, the :: operator can be described like this (from this SO question):


方法参考可以用不同的样式获得,但它们都意味着相同:

Method Reference can be obtained in different styles, but they all mean the same:


  1. 静态方法( ClassName :: methodName

  2. 特定对象的实例方法( instanceRef :: methodName

  3. a的超级方法特定对象( super :: methodName

  4. 特定类型的任意对象的实例方法( ClassName :: methodName

  5. 类构造函数引用( ClassName :: new

  6. 数组构造函数引用( TypeName [] :: new

  1. A static method (ClassName::methodName)
  2. An instance method of a particular object (instanceRef::methodName)
  3. A super method of a particular object (super::methodName)
  4. An instance method of an arbitrary object of a particular type (ClassName::methodName)
  5. A class constructor reference (ClassName::new)
  6. An array constructor reference (TypeName[]::new)


所以,这意味着方法引用 String :: compareToIgnoreCase 属于第二个类别gory( instanceRef :: methodName )这意味着它可以被翻译成(a,b) - > a.compareToIgnoreCase(b)中

So, that means that the method reference String::compareToIgnoreCase falls under the second category (instanceRef::methodName) which means that it can be translated to (a, b) -> a.compareToIgnoreCase(b).

我相信以下示例进一步说明了这一点。 Comparator< String> 包含一个对两个 String 操作数进行操作的方法,并返回 int 。这可以伪描述为(a,b)==> return int (其中操作数是 a b )。如果您按照这种方式查看以下所有内容:

I believe that the following examples illustrates this further. A Comparator<String> contains one method that operates on two String operands and returns an int. That can be pseudo-described as (a, b) ==> return int (where the operands are a and b). If you view it that way all of the following falls under that category:

// Trad anonymous inner class
// Operands: o1 and o2. Return value: int
Comparator<String> cTrad = new Comparator<String>() {
    @Override
    public int compare(final String o1, final String o2) {
        return o1.compareToIgnoreCase(o2);
    }
};

// Lambda-style
// Operands: o1 and o2. Return value: int
Comparator<String> cLambda = (o1, o2) -> o1.compareToIgnoreCase(o2);

// Method-reference à la bullet #2 above. 
// The invokation can be translated to the two operands and the return value of type int. 
// The first operand is the string instance, the second operand is the method-parameter to
// to the method compareToIgnoreCase and the return value is obviously an int. This means that it
// can be translated to "instanceRef::methodName".
Comparator<String> cMethodRef = String::compareToIgnoreCase;

这个很好的答案解释了如何编译lambda函数。在那个答案中, Jarandinor 引用了Brian Goetz的以下段落,该文档描述了有关lambda翻译的更多信息

This great SO-answer to explains how lambda functions are compiled. In that answer Jarandinor refers to the following passage from Brian Goetz excellent document that describes more about lambda translations.


我们不是生成字节码来创建实现lambda表达式的对象(例如调用内部类的构造函数),而是描述构造lambda的配方,并将实际构造委托给语言运行库。该配方在invokedynamic指令的静态和动态参数列表中编码。

Instead of generating bytecode to create the object that implements the lambda expression (such as calling a constructor for an inner class), we describe a recipe for constructing the lambda, and delegate the actual construction to the language runtime. That recipe is encoded in the static and dynamic argument lists of an invokedynamic instruction.

基本上这意味着本机运行时决定如何转换lambda。

Basically what this means is that the native runtime decides how to translate the lambda.

Brian继续:


方法引用被视为与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.

所以,lambdas desugared 进入新方法。例如。

So, lambdas are desugared into a new method. E.g.

class A {
    public void foo() {
        List<String> list = ...
        list.forEach( s -> { System.out.println(s); } );
    }
}

上面的代码将 desugared 是这样的:

The code above will be desugared to something like this:

class A {
    public void foo() {
        List<String> list = ...
        list.forEach( [lambda for lambda$1 as Consumer] );
    }

    static void lambda$1(String s) {
        System.out.println(s);
    }
}

但是,Brian也在文档中解释了这一点: / p>

But, Brian also explains this in the document:


如果desugared方法是实例方法,则接收器被认为是第一个参数

if the desugared method is an instance method, the receiver is considered to be the first argument

Brian继续解释 lambda的剩余参数作为参数传递给被引用的方法

Brian continues to explan that the lambda’s remaining arguments are passed as arguments to the referred method.

所以,在 Moandji Ezana的这篇文章的帮助下,将$ code> compareToIgnoreCase 作为比较器< String> 进行分解,可以分解为以下步骤:

So, with the help of this entry by Moandji Ezana, the desugaring of compareToIgnoreCase as a Comparator<String> can be broken down to the following steps:


  • 列表< String> >预计 Comparator< String>

  • Comparator< String> 是一个函数接口的方法是 int sort(String,String),这是等价的adnt to BiFunction< String,String,Integer>

  • 比较器实例因此可以由 BiFunction提供 -compatible lambda:(String a,String b) - > a.compareToIgnoreCase(b)

  • String :: compareToIgnoreCase 指的是一个带<的实例方法code> String 参数,因此它与上面的lambda兼容: String a 成为接收者并且 String b 成为方法参数

  • Collections#sort for a List<String> expects a Comparator<String>
  • Comparator<String> is a functional interface with the method int sort(String, String), which is equivalent to BiFunction<String, String, Integer>
  • The comparator instance could therefore be supplied by a BiFunction-compatible lambda: (String a, String b) -> a.compareToIgnoreCase(b)
  • String::compareToIgnoreCase refers to an instance method that takes a String argument, so it is compatible with the above lambda: String a becomes the receiver and String b becomes the method argument

编辑:从OP输入后我添加了低级别示例,解释了 desugaring

After input from the OP I have added a low level example that explains the desugaring

这篇关于在Java8中引用具有不同参数的方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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