Java Lambda 表达式

Java Lambda Expression

我目前正在 JDK 1.8 学习 lambda 表达式。我遇到了一些我发现我不理解的代码。

代码如下:

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.lang.Comparable;

/**
 * Hello world!
 *
 */
public class App
{
    public static void main( String[] args ) throws Exception
    {

        List<String> list = Arrays.asList("a", "b", "c");
        sort(list, Comparable::<String>compareTo);

    }

    interface MyComparable {
        public <T extends Comparable<T>> int compare(T obj1, T obj2 );
    }

    public static <T extends Comparable<T>> void sort(List<T> list, MyComparable comp) {

        int n = comp.compare("5","2");
        System.out.println(n);
    }

}

comp.compare("5", "3")最终执行"5".compareTo("2")。 我的理解是编译器需要找到一个与

具有相同签名的静态方法
public <T extends Comparable<T>> int compare(T obj1, T obj2 );

我已经创建了这样一个方法并且它有效。我不明白为什么 java 编译器调用 "5".compareTo("2")。他们的方法签名不一样

关于编译器为何生成此类代码的任何信息?

如果您正在尝试学习方法参考,您应该求助于某种学习方式 material,例如Oracle’s Java tutorial。你会发现:

Kinds of method references

Kind Example
Reference to a static method ContainingClass::staticMethodName
Reference to an instance method of a particular object containingObject::instanceMethodName
Reference to an instance method of an arbitrary object of a particular type ContainingType::methodName
Reference to a constructor ClassName::new

所以你看,方法引用并不局限于static个方法。

您的方法引用 Comparable::<String>compareTo 匹配“对特定类型的任意对象的实例方法的引用”类型。

此时值得注意的是,您实际上是在引用方法 Comparable.compareTo,就好像您已经编写了 Comparable::compareTo。由于引用的方法本身没有类型参数,因此您的类型参数无效。例如。你可以写 Comparable::<Button>compareTo 得到相同的结果。

引用的方法具有函数签名 (Comparable,Comparable) → int,因为它在一个 Comparable 上调用 Comparable.compareTo 时消耗两个 Comparable,传递第二个 Comparable作为参数(它将 return 和 int)。这与您的 interface

的功能签名相匹配
interface MyComparable {
    public <T extends Comparable<T>> int compare(T obj1, T obj2 );
}

因此可以在此上下文中使用方法参考。

我简化了功能签名;实际上它们是 (T,T)→int 使用 <T extends Comparable<T>>,因此您只能使用此函数比较 相同 具体 Comparable 实现的两个实例。

所以您想知道如何将签名与预期不同的方法引用作为 lambda 表达式发送。但它们不需要完全相同。基本上,只有 参数列表 return 类型 很重要:

A lambda expression can be assigned to a target type T if all of the following conditions hold:

  • T is a functional interface type

  • The lambda expression has the same number of parameters as T's method, and those parameters' types are the same

  • Each expression returned by the lambda body is compatible with T's method's return type

  • Each exception thrown by the lambda body is allowed by T's method's throws clause

from here (section 4)

您的 compare() (String, String) 的参数列表可以与 String#compareTo() (this, String) 和它们的 return 类型也是相同的 (int),因此当需要另一个时,一个可以用作 lambda。