Подстановочный знак Java с супер неожиданным поведением

1

Я написал класс 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);

  }
}
  • 0
    Единственное, что беспокоит - это посторонние поля name в дочерних классах, которые остаются нулевыми и скрывают Fruit.name . Вы должны сделать поле окончательным, как в protected final String name; во Fruit, поэтому его нельзя изменить, и сопоставимый контракт остается в силе.
  • 0
    Возможный дубликат Что такое <? супер T> синтаксис?
Теги:
wildcard
super

2 ответа

2

В вашем случае 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>.

0

Поскольку Fruit реализует Comparable а Apple и Orange расширяют Fruit, тогда они просто наследуют реализацию compareTo от Fruit.

Вы можете переопределить этот метод compareTo в подклассах Apple и Orange если хотите.

Я думаю, что это ваше понимание наследования здесь, а не ваше понимание символа >.

<T extends Comparable<? super T>>

Здесь вы определяете, что тип T расширяет Comparable, поэтому вы можете безопасно вызвать метод compareTo для объектов.

Оба объекта, которые вы передали методу DO, расширяют сопоставимые (поскольку они расширяют Fruit, который реализует Comparable)

  • 0
    Извините, может быть, мой вопрос не был ясен. Почему всегда указывается этот подстановочный знак <T extends Comparable <? супер T >>? если это все же работает?
  • 0
    Это просто говорит компилятору разрешать передачу в этот метод только тех объектов, которые расширяют интерфейс COmparable. Без него вы не могли бы вызвать метод CompareTo, поскольку вы не знали бы, реализует ли его параметр. Но все ваши объекты это реализуют.
Показать ещё 4 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню