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

时间:2015-04-19 20:00:06

标签: java lambda java-8

我阅读了有关Lambda表达式的Java 8教程,并且不太了解"对特定类型的任意对象的实例方法的引用的方法参考示例"

在同一个教程中,有一个例子"参考特定对象的实例方法"看起来很喜欢。

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)接受两个参数并使用相同的两个参数调用方法。

现在"引用特定类型的任意对象的实例方法"示例使用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)我不明白编译器如何决定Arrays.sort第二个参数的可接受性。我想也许它足够聪明,可以理解如何调用该方法,所以我创建了一个例子。

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方法相同

2 个答案:

答案 0 :(得分:6)

FakeString中,您的compareToIgnoreCase有一个String参数,因此无法取代Comparator<String>,这需要一个带有两个String参数的方法

FakeString2中,您的compareToIgnoreCase有一个隐含的FakeString参数(this)和一个String参数,因此,它再次无法取代Comparator<String> }。

答案 1 :(得分:6)

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

首先是Oracle示例

让我们看看第一个案例:

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

这里,方法compareByName在传递的myComparisonProvider实例中调用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[]进行排序,因此在compareToIgnoreCase实例上调用方法String,当前正在使用其他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上调用此方法。因此,我们必须在static上调用FakeString方法。但我们也不能这样做,因为我们需要一个方法(String, String) -> int,但我们只有(String) -> int - 编译错误。

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

TL; DR:

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