Понимание верхней и нижней границ? в Java Generics

мне действительно трудно понять параметр wild card. У меня есть несколько вопросов по этому поводу.

  1. ? в качестве параметра типа можно использовать только в методах. например: printAll(MyList<? extends Serializable>) Я не могу определить классы с ? параметр, как тип.

  2. я понимаю верхнюю границу ?. printAll(MyList<? extends Serializable>) означает: "printAll печати MyList если у него есть объекты, реализующие Serialzable взаимодействие."
    У меня есть небольшая проблема с super. printAll(MyList<? super MyClass>) означает: "printAll печати MyList если у него есть объекты MyClass или любой класс, который расширяет MyClass (потомки MyClass)."

поправьте меня, где я ошибся.

короче, только T или E или K или V или N может использоваться в качестве параметров типа для определения универсальных классов. ? только использовать в методах


обновление 1:
public void printAll(MyList<? super MyClass>){
    // code code code
}

согласно Книге Айвора Хортона,MyList<? super MyClass> означает, что я могу напечатать MyList если у него есть объекты MyClass или любой из интерфейсов или классов, которые он реализует. То есть, MyClass это нижняя граница. Это последний класс в иерархии наследования. Это значит, что мое первоначальное предположение было неверным.

так, скажем, если MyClass похоже,:

public class MyClass extends Thread implements ActionListener{
    // whatever
}

затем, printAll() будет печатать, если
1. Есть объекты MyClass в списке
2. Есть объекты Thread или ActionListener на List


обновление 2:

Итак, прочитав множество ответов на вопрос, вот мое понимание:

  1. ? extends T означает любой класс, который расширяет T. Таким образом, мы имеем в виду дети T. Следовательно,T - верхняя граница. Самый верхний класс в иерархии наследования

  2. ? super T означает любой класс / интерфейс, который super of T. Таким образом, мы имеем в виду все родителям T. T таким образом, нижняя граница. Самый низкий класс в иерархии наследования

6 ответов


? в качестве параметра типа можно использовать только в методах. например: printAll(MyList<? extends Serializable>) Я не могу определить классы с ? параметр, как тип.

шаблон (?) не является формальным параметром типа, А может использоваться как аргумент типа. В приведенном примере,? extends Serializable приведен в качестве аргумента типа для универсального типа MyList о printAll параметр метода.

методы можно также объявить тип параметры как классы, например:

static <T extends Serializable> void printAll(MyList<T> myList)

я понимаю верхнюю границу ?. printAll(MyList<? extends Serializable>) означает printAll будет печатать MyList, если у него есть объекты, реализующие интерфейс Serialzable

точнее, это означает вызов printAll будет компилироваться, только если он принят MyList С некоторым общим типом, который является или реализует Serializable. В этом случае он примет MyList<Serializable>, MyList<Integer>, так далее.

у меня есть небольшая проблема с super. printAll(MyList<? super MyClass>) означает printAll будет печатать MyList, если у него есть объекты MyClass или любой класс, который расширяет MyClass (потомки MyClass)

подстановочный знак, ограниченный super это ниже связаны. Чтобы мы могли сказать вызов printAll будет компилироваться, только если он принят MyList С некоторым общим типом, который MyClass или какой-то супер-типа MyClass. Так что в этом случае он примет MyList<MyClass>, например,MyList<MyParentClass> или MyList<Object>.

Итак, скажите, если MyClass выглядит так:

public class MyClass extends Thread implements ActionListener{
    // whatever
}

затем printAll () напечатает if

  1. в списке есть объекты MyClass
  2. в списке есть объекты потока или ActionListener

вы на правильном пути. Но я думаю, что, например, " он будет печатать, если есть объекты MyClass в списке" проблематично. Это звучит так, как будто вы определяете поведение среды выполнения - дженерики все о проверках времени компиляции. Например, не смог бы передать MyList<MySubclass> в качестве аргумента MyList<? super MyClass>, хотя он может содержать экземпляры MyClass, в порядке наследования. Я бы перефразировал его так:--54-->

вызов printAll(MyList<? super MyClass>) будет компилироваться, только если она передается a:

  1. MyList<MyClass>
  2. MyList<Thread>
  3. MyList<Runnable>
  4. MyList<ActionListener>
  5. MyList<EventListener>
  6. MyList<Object>
  7. MyList<? super X> здесь X is MyClass, Thread, Runnable, ActionListener, EventListener или Object.

Итак, после прочтения многих ответов на вопрос, вот мой понимание:

? extends T означает любой класс, который расширяет T. Таким образом, мы имеем в виду следовательно, дети т.--122-->T-верхняя граница. Высший класс в иерархии наследования

? super T означает любой класс / интерфейс, который super т. Таким образом, мы обращаясь ко всем родителям т. T, таким образом, нижняя граница. Этот самый низкий класс в иерархии наследования

близко, но я бы не сказал "Дети T" или "родители T", так как эти границы включительно - точнее было бы сказать"T или его подтипов" и "T или его супертипов".


в первую очередь T или E или K или что-то не фиксированные имена. Это просто переменные типа, и вы сами решаете, как их назвать. T, E, K - это просто примеры, но вы можете назвать это Foo или что-то еще.

сейчас на ваш первый вопрос: после подстановки ? представляет тип "any and unknown", неуказанный, нет смысла объявлять класс generic над неуказанным типом. Полезно иметь подстановочный знак в параметров методов или переменных, когда вы не заботитесь о типе.

Теперь относительно вашего второго вопроса: нижняя граница дает еще большую гибкость вашим общим методам. оба!--8--> и super, напротив

  • ? extends T: неизвестный тип, который является подтипом T
  • ? super T: неизвестный тип, который является супер типа T

последнее может быть полезно, когда вы хотите принять тип это совместимо с T (так что T-это тот тип). Практический пример можно найти здесь.


начнем с самого начала.

строго говоря любой допустимый идентификатор java может использоваться как параметр универсального типа - это просто специальный тип переменной:

public static final class MyGenericClass<MyGenericType> {

}

является совершенно действительным Java.

Далее, вы можете использовать ? везде, где вы можете сделать заявление. Вы можете использовать подстановочный знак при объявлении переменных, но не при создать них:

public static final class MyGenericClass {
    private final Collection<? extends String> myThings;

    public MyGenericClass(Collection<? extends String> myThings) {
        this.myThings = myThings;
    }  

    public void doStuff(final Collection<? extends String> myThings) {

    }
}

снова все действительно, Вы не может этого:

final Collection<? extends String> myThings = new ArrayList<? extends String>();

когда дело доходит до extends vs super это называется co-дисперсия против contra-дисперсии. Он определяет, в каком направлении по иерархии классов типов могут путешествовать:

final Collection<? extends Runnable> example1 = new ArrayList<Runnable>();
final Collection<? extends Runnable> example2 = new ArrayList<TimerTask>();
final Collection<? super Runnable> example3 = new ArrayList<Runnable>();
final Collection<? super Runnable> example4 = new ArrayList<Object>();

первые два примера демонстрируют extends - плотно связаны, можно предположить, с Collection и Runnable как пользователь может пройти Collection что есть Runnable в его иерархии наследования.

вторые два примера демонстрируют super - плотно связаны, можно предположить, с Collection и Object как мы разрешаем все, что есть на иерархия наследования Runnable.


Хммм ваше заявление на super ( printAll(MyList<? super MyClass>) ) не понятно. Что это значит, если предположить, что Myclass расширяется Object вы можете printAll(MyList<MyClass>) и printAll(MyList<Object>) но ничего больше.... это означает, что общий тип MyList должен быть суперклассом (а не подклассом) MyClass. Это отличается от того, что ты сказал.


для первого вопроса: Вы не можете определить метод с ? в качестве параметра тип. Следующее не будет компилироваться:

void <?> foo() {}

? используется для привязки к другим дженерикам без указания параметра type. Вы можете написать для методов:

void foo(List<?> e) {}

и вы также можете писать на классы:

public class Bar<E extends List<?>> { }

использовать super:

public void printAll(MyList<? super MyClass>){
    // code code code
}

это не будет, как вы говорите, печатать список " если у него есть объекты Класса MyClass". Он может иметь объекты любого класса, который является подклассом класса, который является родителем MyClass. Компилятор не знает во время компиляции, какие объекты будут в списке в любом случае.

чтобы обойти это, рассмотрим простой пример с Number иерархия классов. Float и Integer дети Number. Вы можете написать свой метод так:

public void printAll(List<? super Float>){
    // code code code
}

тогда вы можете вызвать этот метод с помощью List<Number>:

List<Number> numbers = new ArrayList<>();
numbers.add(1); // actually only add an Integer
printAll(numbers); // compiles.

это возможно, было бы не очень полезно в этом случае. Где было бы полезно, например, когда вы хотите добавить Float в коллекцию, не желая, чтобы это был только список, например:

public void addFloat(List<? super Float> list){
    list.add(2.5);
}

? также может использоваться как метод return type

List<? extends Number> xxx() {
    ...
}

или как поле или переменную типа

List<? extends Number> xxx = ...
List<? super Nember> xxx = ...