【问题标题】:Java 8 and method references - specifically compareToIgnoreCaseJava 8 和方法参考——特别是 compareToIgnoreCase
【发布时间】:2015-04-19 20:00:05
【问题描述】:

我阅读了有关 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 方法相同的方法也无法编译

【问题讨论】:

标签: java lambda java-8


【解决方案1】:

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

首先是 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[] 进行排序,因此在当前正在排序的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) -&gt; int,但我们只有(String) -&gt; int - 编译错误。

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

TL;DR:

您缺少的一点是在String::compareToIgnoreCase 示例中;在String 上调用该方法当前正在处理

【讨论】:

  • 赞成 tl;dr.我相信这是 OP 缺少的主要重点。
  • 我刚刚得到它。不得不读了好几遍。尽管它确实有道理,但 Oracle 官方网站缺乏关于这个主题的更多解释。你不能把它扔在一条线上,并期望每个人都能理解它。这只是我的诚实意见。
【解决方案2】:

FakeString 中,您的compareToIgnoreCase 有一个String 参数,因此它不能代替Comparator&lt;String&gt;,后者需要一个带有两个字符串参数的方法。

FakeString2 中,您的compareToIgnoreCase 有一个隐含的FakeString 参数(this)和一个字符串参数,因此,它不能代替Comparator&lt;String&gt;

【讨论】:

  • 但是String#compareToIgnoreCase也只有一个字符串参数。
  • @Jean-FrançoisSavard String#compareToIgnoreCase,就像String#compareTo 有一个隐式的第二个字符串参数 - this,它允许在需要具有两个字符串参数的方法时使用它。
  • 我认为,如果您根据implicit 参数的想法进行开发,您将得到预期的答案。我也有兴趣阅读它,即使现在我知道它为什么起作用了
  • @Eran 我知道,正如 Dici 也提到的,我认为这是答案的一个重要点,这就是我提到它的原因。不知道的人不会理解你的句子。
  • @user1169587 对于每个类的每个实例方法,您可以说调用该方法的实例是该方法的隐式参数。没有理由在 Javadoc 中提及它,因为它不是一个实际的论点。当您使用对实例方法的方法引用时,隐式参数变得很重要。
猜你喜欢
  • 1970-01-01
  • 2022-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-09
  • 1970-01-01
  • 2013-11-19
  • 2016-12-25
相关资源
最近更新 更多