Java 8 и ссылки на методы - в частности compareToIgnoreCase

Я прочитал учебник 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. Учебник не очень ясен, но, похоже, подразумевает, что вы в конечном итоге получаете лямбда, например (a, b) → a.compareToIgnoreCase(b) Я не понимаю, как компилятор решает, что приемлемо для второго параметра массива Array.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);
 }
 }
}
</string>

Может кто-нибудь объяснить, почему эти два массива Arrays.sort не компилируются, хотя они используют методы, которые аналогичны методу String.compareToIgnoreCase

2 ответа

В FakeString у вашего compareToIgnoreCase есть один аргумент String, поэтому он не может быть вместо Comparator, для которого требуется метод с двумя аргументами String.

В FakeString2 ваш compareToIgnoreCase имеет неявный аргумент FakeString (this) и аргумент String, поэтому он снова не может быть вместо Comparator.


Это различие между ссылкой метода на некоторый объект и ссылкой метода на обрабатываемый объект.

Сначала примеры Oracle

Давайте рассмотрим этот первый случай:

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

Здесь метод compareByName вызывается в переданном экземпляре myComparisonProvider с каждой парой аргументов в алгоритме sort.

Итак, при сравнении a и b мы фактически вызываем:

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 в качестве аргумента.

Итак, при сравнении a и b мы фактически вызываем:

final int c = a.compareToIgnoreCase(b);

Итак, это два разных случая:

  • где вы передаете метод на экземпляр произвольного объекта; и
  • где вы передаете метод, который должен быть вызван в обрабатываемом экземпляре.

На примерах

Теперь в первом примере у вас также есть String[], и вы пытаетесь его сортировать. Итак:

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

Итак, при сравнении a и b мы фактически вызываем:

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

Единственное отличие: compare is static.

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

Теперь String[] не является FakeString[], поэтому мы не можем вызвать этот метод на String. Поэтому мы должны вызывать метод static на FakeString. Но мы не можем этого сделать, потому что нам нужен метод (String, String) -> int, но мы имеем только (String) -> int - ошибку компиляции.

Во втором примере проблема точно такая же, как у вас все еще есть String[]. И compareToIgnoreCase имеет неправильную подпись.

TL; DR:

То, что вам не хватает, это пример String::compareToIgnoreCase; метод вызывается при обработке String.

licensed under cc by-sa 3.0 with attribution.