Я написал класс Fruit, который реализует интерфейс Comparable и 2 подкласса: Apple и Orange. Я написал метод, который возвращает максимум между двумя фруктами (что бы это ни значило).
Заметьте, что я НЕ использовал никаких подстановочных знаков с супер.
Я думал, что метод max завершится неудачно, потому что интерфейс Comparable не реализован непосредственно Apple или Orange.
Вопрос: Почему предлагается использовать эту форму шаблона:
<T extends Comparable<? super T>>
если он работает и без супер?
Вот код:
package main;
//Note that I did not use the super wildcard: <T extends Comparable<? super T>>
class Max {
public static <T extends Comparable<T>> T getMax(T x, T y) {
return x.compareTo(y) >= 0 ? x : y;
}
}
class Fruit implements Comparable<Fruit> {
public String name;
public Fruit(String name) {
this.name = name;
}
@Override
public int compareTo(Fruit other) {
return name.compareTo(other.name) == 0 ? 0 :
name.compareTo(other.name) > 0 ? 1 : -1;
}
}
class Apple extends Fruit {
String name;
public Apple(String name) {
super(name);
}
}
class Orange extends Fruit {
String name;
public Orange(String name) {
super(name);
}
}
public class Main {
public static void main(String[] args) {
Apple a = new Apple("apple");
Orange o = new Orange("orange");
Fruit f = Max.getMax(a, o); //It should not be allowed because T does not implement Comparable directly
System.out.println(f.name);
}
}
В вашем случае T
можно выбрать как Fruit
, а утверждение Fruit f = Max.getMax(a, o);
будет правильно проверять тип. Поэтому он работает.
Max.getMax()
имеет типы параметров T
, а экземпляры подтипов T
также являются экземплярами T
, поэтому метод принимает любые подтипы T
качестве аргументов.
Обратите внимание, однако, что ваш метод по-прежнему имеет проблему, заключающуюся в том, что T
можно определить только как Fruit
, а не Apple
, поэтому вы не можете вернуть Apple
:
Apple a = Max.getMax(apple1, apple2);
Однако рассмотрим что-то, где T
является типичным параметром типа:
public static <T extends Comparable<T>> T getMax(List<T> xs) {
//...
}
Дженерики не ковариантны, поэтому этот метод может принимать только List<Fruit>
, но не List<Apple>
или List<Orange>
, хотя Apple
может сравниться с Apple
s и т.д.
Если вы измените его на:
public static <T extends Comparable<? super T>> T getMax(List<T> xs) {
//...
}
то он будет работать для List<Apple>
и List<Orange>
.
Поскольку Fruit реализует Comparable
а Apple
и Orange
расширяют Fruit, тогда они просто наследуют реализацию compareTo
от Fruit
.
Вы можете переопределить этот метод compareTo в подклассах Apple
и Orange
если хотите.
Я думаю, что это ваше понимание наследования здесь, а не ваше понимание символа >
.
<T extends Comparable<? super T>>
Здесь вы определяете, что тип T
расширяет Comparable, поэтому вы можете безопасно вызвать метод compareTo
для объектов.
Оба объекта, которые вы передали методу DO, расширяют сопоставимые (поскольку они расширяют Fruit, который реализует Comparable)
name
в дочерних классах, которые остаются нулевыми и скрываютFruit.name
. Вы должны сделать поле окончательным, как вprotected final String name;
во Fruit, поэтому его нельзя изменить, и сопоставимый контракт остается в силе.