Подтвердить что ты не робот

В Java, что может делать дикая карта, которую обычные дженерики не могут сделать?

Я новичок в Java. В этот документ они дают это как прецедент для использования подстановочного знака:

static void printCollection(Collection c) {
    Iterator i = c.iterator();
    for (int k = 0; k < c.size(); k++) {
        System.out.println(i.next());
    }
}

Это их решение:

static void printCollection(Collection<?> c) {
    for (Object e : c) {
        System.out.println(e);
    }
}

Но я мог бы сделать то же самое без wild card:

static <T> void printCollection(Collection<T> c) {
    Iterator i = c.iterator();
    for (int k = 0; k < c.size(); k++) {
        System.out.println(i.next());
    }
}

Может ли кто-нибудь показать мне простой случай использования, когда обычные дженерики не будут работать, но дикая карта будет?

Обновить: ответы здесь Когда использовать подстановочные знаки в Java Generics? НЕ говорите нам о необходимости подстановки. На самом деле это наоборот.

4b9b3361

Ответ 1

Мы можем использовать только одну подстановочную карточку: объявлять типы, агностические по отношению к определенному параметру типа, например, "список любого вида списка":

List<List<?>> listOfAnyList = ...;

listOfAnyList.add( new ArrayList<String>() );
listOfAnyList.add( new ArrayList<Double>() );

Это невозможно без подстановочного знака: * потому что списки элементов могут иметь разные типы друг от друга.

И если мы попытаемся capture, мы обнаружим, что не можем:

static <E> void m(List<List<E>> listOfParticularList) {}

m( listOfAnyList ); // <- this won't compile

Еще одна особенность шаблонов позволяет нам делать такие параметры, которые не могут установить нижнюю границу. (Параметр типа может быть объявлен с привязкой extends, но не с super. **)

class Protector {
    private String secretMessage = "abc";

    void pass(Consumer<? super String> consumer) {
        consumer.accept( secretMessage );
    }
}

Предположим, что pass было объявлено, чтобы взять Consumer<String>. Предположим теперь, что у нас был Consumer<Object>:

class CollectorOfAnything implements Consumer<Object> {
    private List<Object> myCollection = new ArrayList<>();

    @Override
    public void accept(Object anything) {
        myCollection.add( anything );
    }
}

Проблема заключается в следующем: мы не можем передать ее методу, принимающему Consumer<String>. Объявление Consumer<? super String> означает, что мы можем передать любого потребителя, который принимает String. (Также см. Java Generics: Что такое PECS?.)

В большинстве случаев подстановочные знаки позволяют нам делать аккуратные объявления.

Если нам не нужно использовать тип, нам не нужно объявлять для него параметр типа.


* Технически также возможно использование raw type, но необработанные типы обескуражены.

** Я не знаю, почему Java не разрешает super для параметра типа. 4.5.1. Тип Аргументы параметризованных типов могут означать, что это имеет какое-то отношение к ограничению вывода типа:

В отличие от обычных переменных типа, объявленных в сигнатуре метода, при использовании подстановочного символа не требуется вывод типа. Следовательно, допустимо объявлять нижние границы на подстановочном знаке [& hellip;].

Ответ 2

T обозначает общий тип этой структуры данных. В последнем примере вы не используете его, а его НЕ - фактический тип (например, String), а потому, что вы его не используете, в данном случае это не имеет особого значения.

Например, если у вас была коллекция и пыталась передать ее методу, который принимает коллекцию, это работает, потому что нет типа T в пути к классам, поэтому он считается переменной. Если вы попытались передать одну и ту же коллекцию методу, который принимает коллекцию, это не сработает, потому что у вас есть String в вашем пути к классам, поэтому это не переменная.

Ответ 3

В качестве примера возьмите Список.

  • List<?> может быть родительским классом List<A>.

например,

List<B> bList = new ArrayList<>(); // B is a class defined in advance
List<?> list = bList;

вы никогда не сможете использовать <T> в этой ситуации.

  • <?> имеет подстановочный знак.

здесь

  void foo(List<?> i) {
        i.set(0, i.get(0));
    }

приведенный выше код не может быть скомпилирован. Вы можете исправить это:

    void foo(List<?> i) {
        fooHelper(i);
    }

    // wildcard can be captured through type inference.
    private <T> void fooHelper(List<T> l) {
        l.set(0, l.get(0));
    }

см. больше, http://docs.oracle.com/javase/tutorial/java/generics/capture.html

Я могу только думать о двух в настоящее время, позже может обновляться.