Java-дженерики (макіяж)


109

У мене є кілька запитань щодо загальних підстановок на Яві:

  1. Яка різниця між List<? extends T>іList<? super T> ?

  2. Що таке обмежений підстановочний код і що таке необмежений підстановочний знак?


У тому випадку, коли Тед Гао відповідь видаляється (оскільки вона була посилання тільки), ось блог , до якого він пов'язаний.
royhowie

Відповіді:


123

У першому питанні, <? extends T>і <? super T>є прикладами обмежених групових символів. Необов’язаний підстановочний вигляд виглядає <?>, і в основному означає <? extends Object>. Це вільно означає, що родова може бути будь-якого типу. Обмежений підстановочний знак ( <? extends T>або <? super T>) встановлює обмеження на тип, кажучи, що він або повинен поширювати певний тип ( <? extends T>відомий як верхня межа), або повинен бути родоначальником певного типу ( <? super T>відомий як нижня межа) .

Навчальні програми Java містять досить непогані пояснення щодо дженериків у статтях Wildcards та More Fun with Wildcards .


Просто щоб правильно це зробити, якщо A <B і B <C, то: <A розширює C> неправильно?
Пабло Фернандес

Якщо під A <B ви маєте на увазі A поширює B, то A дійсно розширює C. Ви б не використовували це в синтаксисі підстановки, ви б сказали <? розширює C>, щоб обмежити свій вибір або A, або B.
Білл Ящірка

і в такому випадку, якщо я скажу <? super C> яка б різниця?
Пабло Фернандес

3
Просто хотів порекомендувати ще одне посилання на Java Generics: angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html
Зак Скривена

3
@Pablo: <? super C>означає, що ваш тип обмежений чимось вище Cв ієрархії типів. (Вибачте за надзвичайно пізню відповідь. Гадаю, у нас не було сповіщень про коментарі 2 роки тому?)
Білл Ящірка

49

Якщо у вас є ієрархія класів A, B - це підклас A, а C і D - це підклас B, як показано нижче

class A {}
class B extends A {}
class C extends B {}
class D extends B {}

Тоді

List<? extends A> la;
la = new ArrayList<B>();
la = new ArrayList<C>();
la = new ArrayList<D>();

List<? super B> lb;
lb = new ArrayList<A>(); //fine
lb = new ArrayList<C>(); //will not compile

public void someMethod(List<? extends B> lb) {
    B b = lb.get(0); // is fine
    lb.add(new C()); //will not compile as we do not know the type of the list, only that it is bounded above by B
}

public void otherMethod(List<? super B> lb) {
    B b = lb.get(0); // will not compile as we do not know whether the list is of type B, it may be a List<A> and only contain instances of A
    lb.add(new B()); // is fine, as we know that it will be a super type of A 
}

Обмежений підстановочний знак - це те, ? extends Bде B - якийсь тип. Тобто тип невідомий, але на ньому може бути розміщена "прив'язка". У цьому випадку він обмежений деяким класом, який є підкласом В.


Я припускаю, List<? super B>описує, як List приймає тип, який є батьківським класом класу B ? Чому C і D не збирають hmm?
Volkan Güven

38

Джош Блох також має гарне пояснення, коли його використовувати, superі extendsв цій відео-розмові Google про io, де він згадує про мнемонічний виробник extendsспоживачівsuper .

З слайдів презентації:

Припустимо, ви хочете додати масові методи Stack<E>

void pushAll(Collection<? extends E> src);

- src - виробник E

void popAll(Collection<? super E> dst);

- dst - споживач E


Я читав книгу Блоха, але все ще не бачу різниці між розширеннями і супер в цьому конкретному випадку.
Пабло Фернандес

Подивіться відео, я думаю, це досить зрозуміло. Крім того, я думаю, вам слід задати ще одне запитання щодо цього: "в чому різниця між списком <? Розширює T> та списком <? Super T>", де ви, сподіваємось, отримаєте більше відповідей. (якщо ви це зробите, додайте посилання звідси)
пусте

2
Ліжка - чому б не включити приклад у цю відповідь, щоб зробити його повноцінним і самостійним. Посилання ефемерні.
Джеймс Шек

3

Можуть бути випадки, коли вам потрібно обмежити типи типів, дозволених для передачі параметру типу. Наприклад, метод, який працює на числах, може захотіти приймати лише екземпляри Number або його підкласи. Для цього призначені параметри обмеженого типу.

Collection<? extends MyObject> 

означає, що він може приймати всіх об'єктів, які мають IS-A зв'язок з MyObject (тобто будь-який об'єкт, який є типом myObject, або ми можемо сказати будь-який об'єкт будь-якого підкласу MyObject) або об'єкт класу MyObject.

Наприклад:

class MyObject {}

class YourObject extends MyObject{}

class OurObject extends MyObject{}

Тоді,

Collection<? extends MyObject> myObject; 

буде приймати лише MyObject або дітей MyObject (тобто будь-який об’єкт типу OurObject або YourObject або MyObject, але не будь-який об'єкт надкласу MyObject).


1

В загальному,

Якщо структура містить елементи з типом форми ? extends E, ми можемо отримати елементи зі структури, але ми не можемо вкласти елементи в структуру

List<Integer> ints = new ArrayList<Integer>();
ints.add(1);
ints.add(2);
List<? extends Number> nums = ints;
nums.add(3.14); // compile-time error
assert ints.toString().equals("[1, 2, 3.14]"); 

Щоб помістити елементи в структуру, нам потрібен інший вид підстановки під назвою Wildcards with super,

 List<Object> objs = Arrays.<Object>asList(2, 3.14, "four");
    List<Integer> ints = Arrays.asList(5, 6);
    Collections.copy(objs, ints);
    assert objs.toString().equals("[5, 6, four]");

    public static <T> void copy(List<? super T> dst, List<? extends T> src) {
          for (int i = 0; i < src.size(); i++) {
                dst.set(i, src.get(i));
         }
    }

1

Загальні підстановки створені для того, щоб зробити методи, які працюють на колекції, більше використані.

Наприклад, якщо метод має параметр List<A>, ми можемо надати лише List<A>цей метод. Даремно цей спосіб функціонує за певних обставин:

  1. Якщо цей метод читає лише об'єкти з List<A>, тоді нам слід дозволити надати List<A-sub>цьому методу. (Тому що A-sub IS A A)
  2. Якщо цей метод лише вставляє об'єкти List<A>, то нам слід дозволити надати List<A-super>цьому методу. (Тому що А - супер)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.