Ограниченный универсальный метод с типом '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);

Мне кажется, что самый простой способ думать об этом - кратко игнорировать связь между типом фрукта и типом фруктового сока, который он производит. Эта ссылка устанавливается в объявлении класса; нам не нужно, чтобы сжать кучу Juicys.

Другими словами, просто параметризуйте по типу Juice что Juicys произведет:

<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()));
Другие вопросы по тегам