Ограниченный универсальный метод с типом 'super'
Согласно литературе, которую я прочитал, у нас есть сочные фрукты, реализующие следующий интерфейс:
public interface Juicy<T> {
Juice<T> squeeze();
}
Используя переменные ограниченного типа, следующий метод собрал бы кучу фруктов и сжал их все:
<T extends Juicy<T>> List<Juice<T>> squeeze(List<T> fruits);
Теперь нам нужны младшие братья и сестры, как показано ниже:
class Orange extends Fruit implements Juicy<Orange>;
class RedOrange extends Orange;
Поэтому я ожидаю, что метод будет выглядеть следующим образом:
<T extends Juicy<T>> List<Juice<? super T>> squeeze(List<? extends T> fruits);
Вместо этого я нахожу подпись метода, как показано ниже:
<**T extends Juicy<? super T>>** List<Juice<? super T>> squeezeSuperExtends(List<? extends T> fruits);
Чем объясняется эта разница?
2 ответа
<? super T>
в <T extends Juicy<? super T>>
есть ли что RedOrange
, который является подклассом Juicy<Orange>
может быть в его пределах.
Представь без <? super T>
первый:
public <T extends Juicy<T>> List<Juice<T>> squeeze(List<T> fruits) {...
Сейчас T
должен быть Juicy<T>
, Класс Orange
это Juicy<T>
, это Juicy<Orange>
, Но класс RedOrange
это не Juicy<T>
, Это не Juicy<RedOrange>
; это Juicy<Orange>
, Поэтому, когда мы пытаемся позвонить squeeze
:
List<RedOrange> redOranges = new ArrayList<RedOrange>();
List<Juice<RedOrange>> juices = squeeze(redOranges);
мы получаем следующую ошибку компилятора:
Inferred type 'RedOrange' for type parameter 'T' is not within its bound; should implement 'Juicy<RedOrange>'.
Если мы разместим <? super T>
, что позволяет параметр типа для Juicy
быть суперклассом T
, Это позволяет RedOrange
использоваться, потому что это Juicy<Orange>
, а также Orange
это суперкласс RedOrange
,
public <T extends Juicy<? super T>> List<Juice<T>> squeeze(List<T> fruits) {...
Теперь вызов squeeze
выше компилирует.
РЕДАКТИРОВАТЬ
Но что, если мы хотим squeeze
List<Juice<Orange>>
из List<RedOrange>
? Это немного сложно, но я нашел решение:
Нам нужен параметр второго типа, чтобы соответствовать Orange
в squeeze
метод:
public <S extends Juicy<S>, T extends Juicy<S>> List<Juice<S>> squeeze(List<T> fruits)
Вот, S
представляет собой Orange
чтобы мы могли вернуться List<Juice<Orange>>
, Теперь мы можем сказать
List<RedOrange> redOranges = new ArrayList<RedOrange>();
List<Juice<Orange>> juices = squeeze(redOranges);
Мне кажется, что самый простой способ думать об этом - кратко игнорировать связь между типом фрукта и типом фруктового сока, который он производит. Эта ссылка устанавливается в объявлении класса; нам не нужно, чтобы сжать кучу Juicy
s.
Другими словами, просто параметризуйте по типу Juice
что Juicy
s произведет:
<T> List<Juice<T>> squeeze(List<? extends Juicy<? extends T>> fruits);
Здесь мы производим список соков на основе распространенного типа супер Juice
(т.е. параметр Juicy
), а не распространенный супер тип фруктов.
Тогда мы получим следующее:
//works
List<Juice<Orange>> b = squeeze(Arrays.asList(new Orange(), new RedOrange()));
//fails as it should
List<Juice<RedOrange>> d = squeeze(Arrays.asList(new RedOrange()));