Вывод типа в методе

Недавно я положил руки на Java 8 и попытался использовать ссылки на методы. Я пробовал разные типы ссылок на методы и застрял в типе "Ссылка на метод экземпляра произвольного объекта определенного типа".

String[] arr = {"First", "Second", "Third", "Fourth"};
Arrays.sort(arr, String::compareToIgnoreCase);

Это работает отлично. Но когда я пытаюсь передать метод определяемого пользователем класса через его тип:

Demo2[] arr = {a, b};
Arrays.sort(arr, Demo2::compare);

Показывает ошибку времени компиляции, поскольку "Нестатический метод не может ссылаться на статический контекст".

Здесь класс Demo2:

public class Demo2 implements Comparator<demo2> {
 Integer i;
 Demo2(Integer i1){
 i = i1;
 }
 public Integer getI() {
 return i;
 }
 @Override
 public int compare(Demo2 o1, Demo2 o2) {
 return o1.getI().compareTo(o2.getI());
 }
}
</demo2>
3 ответа

Как указывал вам greg-449, ваш код имеет ошибку.

Сделав ссылку на метод, например YourObjet::yourMethod, вы создадите ссылку static на метод экземпляра. Таким образом, метод будет вызываться для каждого объекта, и, следовательно, подпись должна отличаться от предыдущей

Код, который будет компилироваться, будет иметь следующий вид:

Demo2[] demos = {a, b};
Arrays.sort(demos, Demo2::compareTo);
public class Demo2 {
 Integer i;
 Demo2(Integer i1){
 i = i1;
 }
 public Integer getI() {
 return i;
 }
 public int compareTo(Demo2 other) {
 return this.getI().compareTo(other.getI());
 }
}

Но, как отметил RealSkeptic, это не правильный способ реализации и сравнения объектов. Вместо этого вы должны указать метод Arrays.sort вместо компаратора:

Arrays.sort(demos, (obj1, obj2) -> obj1.getI().compareTo(obj2.getI()));


Интерфейс Comparator, необходимый для Arrays.sort(T[],Comparator<t>)</t>, имеет метод, который принимает две ссылки на объекты одного и того же типа T и возвращает целое число.

В ссылках метода есть немного "магии". То, что делает Java, заключается в том, чтобы обернуть метод таким образом, чтобы он соответствовал требованию интерфейса.

Интерфейс, конечно, не требует статического метода. Но оболочка может создать метод, который вызывает статический метод, как в первом примере Tutorial:

public static int compareByAge(Person a, Person b) {
 return a.birthday.compareTo(b.birthday);
}

Он обматывает его таким образом, что вы получаете что-то похожее на

new Comparator<person>() {
 @Override
 public int compare(Person a, Person b) {
 return Person.compareByAge(a,b);
 }
}
</person>

Что удовлетворяет интерфейсу.

Но в примере в разделе "Ссылка на метод экземпляра произвольного объекта определенного типа" он обертывает его по-разному. Ему нужен метод, который получает две строки, но имеет метод, который получает только один. Вот как определяется String::compareToIgnoreCase:

public int compareToIgnoreCase(String str)

Но в этом случае это метод экземпляра. То, что сейчас делает Java, потому что этот метод относится к объекту типа String и принимает объект типа String, легко построить вокруг него "обертку", которая превращает его в метод, который принимает два объекта, очень похоже на выражение лямбда:

(String a, String b) -> {
 return a.compareToIgnoreCase( b );
}

Или, если представить себе формальную упаковку как Comparator:

new Comparator<string>() {
 @Override
 public int compare(String a, String b) {
 return a.compareToIgnoreCase(b);
 }
}
</string>

Итак, тот факт, что это метод экземпляра, принадлежащий типу T, принимает тип T и возвращает int, позволяет Java соответствующим образом обертывать его, чтобы он соответствовал интерфейсу Comparator.

Но int compare(Demo2 o1, Demo2 o2) не соответствует этому шаблону. Он принимает два параметра. Если метод принимает два параметра, он должен быть статическим методом для соответствия правилам обертывания - нет способа передать объект "this" в интерфейс Comparator. Поэтому он пытается обернуть его как статический метод и терпит неудачу, поскольку он не является статическим методом.

Нижняя строка: вы получили ошибку, потому что для этого конкретного типа ссылки на метод вам нужен метод экземпляра только с одним параметром того же типа, что и класс.

Как отмечает @Holger в комментарии, если у вас есть новый класс, который вы строите, вы не должны ставить метод сравнения в нем специально для такого рода задач сортировки. Если класс имеет естественный порядок, сделайте его Comparable и используйте Arrays.sort(Object[]). Если это не так, и вам нужно сортировать его иногда на основе любого из его атрибутов, используйте выражение лямбда или Comparator.comparing(Demo2::getI), которое лучше использует существующий геттер для конкретной цели сравнения.


Как соглашение, Comparator реализуется с помощью лямбда-выражения, и он выглядит очень странным, чтобы реализовать его в классе.

Demo2[] array = new Demo2[2];
 array[0] = new Demo2(12);
 array[1] = new Demo2(32);
 Comparator<demo2> demo2Comparator = (e1,e2)->e1.getI().compareTo(e2.getI());
 Arrays.sort(array, demo2Comparator);
</demo2>

licensed under cc by-sa 3.0 with attribution.