Java 8 和方法引用 - 特别是 compareToIgnoreCase

Java 8 and method references - specifically compareToIgnoreCase

我阅读了有关 Lambda 表达式的 Java 8 教程,但不太理解 "Reference to an instance method of an arbitrary object of a particular type"

的方法参考示例

在同一教程中有一个示例 "Reference to an Instance Method of a Particular Object" 看起来很像。

public int compareByName(Person a, Person b) {
        return a.getName().compareTo(b.getName());
}
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);

我可以看到这个工作,因为方法 compareByName 具有与 Comparator.compare 相同的签名,lambda (a, b) -> myComparisonProvider.compareByName(a, b) 接受两个参数并调用一个方法具有相同的两个参数。

现在 "Reference to an instance method of an arbitrary object of a particular type" 示例使用 String::compareToIgnoreCase

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

该方法的签名是 int compareTo(String anotherString),与 Comparator.compare 不同。该教程不是很清楚,但似乎暗示您最终会得到一个 lambda,例如 (a, b) -> a.compareToIgnoreCase(b) 我不明白编译器如何决定 [= 的第二个参数可接受的参数35=] 我想也许理解如何调用该方法就足够聪明了,所以我创建了一个示例。

public class LambdaTest {

    public static void main(String... args) {
        String[] stringArray = { "Barbara", "James", "Mary", "John", "Patricia", "Robert", "Michael", "Linda" };

        Arrays.sort(stringArray, String::compareToIgnoreCase);      // This works

        // using Static methods
        Arrays.sort(stringArray, FakeString::compare);              // This compiles
        Arrays.sort(stringArray, FakeString::compareToIgnoreCase);  // This does not

        // using Instance methods
        LambdaTest lt = new LambdaTest();
        FakeString2 fs2 = lt.new FakeString2();
        Arrays.sort(stringArray, fs2::compare);                 // This compiles
        Arrays.sort(stringArray, fs2::compareToIgnoreCase);     // This does not

        for(String name : stringArray){
            System.out.println(name);
        }
    }

    static class FakeString {
         public static int compareToIgnoreCase(String a) {
             return 0;
         }


        public static int compare(String a, String b) {
            return String.CASE_INSENSITIVE_ORDER.compare(a, b);
        }
    }

    class FakeString2 implements Comparator<String> {
         public int compareToIgnoreCase(String a) {
             return 0;
         }

        @Override
        public int compare(String a, String b) {
            return String.CASE_INSENSITIVE_ORDER.compare(a, b);
        }
   }
}

有人可以解释为什么上面两个 Arrays.sort 即使使用与 String.compareToIgnoreCase 方法

相同的方法也不能编译吗

FakeString 中,您的 compareToIgnoreCase 有一个 String 参数,因此它不能代替 Comparator<String>,它需要一个包含两个参数的方法字符串参数。

FakeString2 中,您的 compareToIgnoreCase 有一个隐含的 FakeString 参数 (this) 和一个 String 参数,因此,它不能代替 Comparator<String>.

这是对某个对象的方法引用与对对象正在处理的方法引用之间的区别

首先是Oracle实例

让我们看看第一个案例:

public int compareByName(Person a, Person b) {
        return a.getName().compareTo(b.getName());
}
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);

此处,方法 compareByNamemyComparisonProvider 的传入实例上使用 sort 算法中的每对参数调用。

所以在这里,当比较ab时,我们实际上调用了:

final int c = myComparisonProvider.compareByName(a,b);

现在,在第二种情况下:

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

您正在对 String[] 进行排序,因此在当前正在排序的 String 实例上调用方法 compareToIgnoreCase,并将另一个 String 作为参数。

所以在这里,当比较ab时,我们实际上调用了:

final int c = a.compareToIgnoreCase(b);

所以这是两种不同的情况:

  • 一个你在任意对象实例上传递一个方法;和
  • 其中一个是您传入要在正在处理的实例上调用的方法。

你的例子

现在在您的第一个示例中,您还有一个 String[] 并尝试对其进行排序。所以:

Arrays.sort(stringArray, FakeString::compare);

所以在这里,当比较ab时,我们实际上调用了:

final int c = FakeString.compare(a, b);

唯一的区别是 comparestatic

Arrays.sort(stringArray, FakeString::compareToIgnoreCase);

现在,String[] 不是 FakeString[],所以我们不能在 String 上调用此方法。因此我们必须在 FakeString 上调用 static 方法。但是我们也不能这样做,因为我们需要一个方法 (String, String) -> int 但我们只有 (String) -> int - 编译错误。

在第二个示例中,问题完全相同,因为您仍然有 String[]compareToIgnoreCase 签名错误。

TL;DR:

你遗漏的一点是在 String::compareToIgnoreCase 例子中;该方法在 String 当前正在处理.

上调用