Дженерики в Java с использованием подстановочных знаков

У меня есть вопрос о дженериках в Java, а именно об использовании подстановочных знаков. У меня есть пример класса GenClass:

public class GenClass<E> {

    private E var;

    public void setVar(E x) {
        var = x;
    }

    public E getVar() {
        return var;
    }
}

У меня есть еще один простой класс:

public class ExampleClass {

}

Я написал следующий тестовый класс:

public class TestGenClass {

    public static void main(String[] str) {

        ExampleClass ec = new ExampleClass();

        GenClass<ExampleClass> c = new GenClass<ExampleClass>();

        c.setVar(ec);
        System.out.println(c.getVar());  // OUTPUT: ExampleClass@addbf1
    }

}

Теперь, если я использую подстановочный знак и пишу в тестовом классе это:

GenClass<?> c = new GenClass<ExampleClass>();

на месте:

GenClass<ExampleClass> c = new GenClass<ExampleClass>();

у компилятора нет проблем с этим новым оператором, однако он жалуется на

c.setVar(ec);

В нем говорится, что «метод (setVar()) неприменим для аргументов (ExampleClass)». Почему я получаю это сообщение?

Я думал, что то, как я использовал подстановочный знак, делает ссылочную переменную c типа GenClass, которая будет принимать в качестве параметра любой класс - на месте E у меня будет любой класс. Это просто объявление переменной. Затем я инициализирую его с помощью

new GenClass<ExampleClass>()

это означает, что я создаю объект типа GenClass, который имеет в качестве параметра класс типа ExampleClass. Итак, я думаю, что теперь E в GenClass будет ExampleClass, и я смогу использовать метод setVar(), передав ему в качестве аргумента что-то типа ExampleClass. Это было мое предположение и понимание, но, похоже, Java это не нравится, и я не прав. Любой комментарий приветствуется, спасибо.


person user42155    schedule 02.02.2009    source источник
comment
Согласно вашей пользовательской истории, вы никогда не принимали ответ. Как вы думаете, вы могли бы начать сейчас? Вы даже получаете 2 очка репутации и бесплатный значок (в первый раз) в рамках сделки. :D   -  person Michael Myers    schedule 05.02.2009


Ответы (2)


Эта точная ситуация описана в Учебнике по Java Generics.

Обратите внимание, что [с подстановочным знаком] мы по-прежнему можем читать элементы из [общего Collection] и присваивать им тип Object. Это всегда безопасно, поскольку независимо от фактического типа коллекции она содержит объекты. Однако добавлять к нему произвольные объекты небезопасно:

Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error

Поскольку мы не знаем, что обозначает тип элемента c, мы не можем добавлять к нему объекты. Метод add() принимает аргументы типа E, типа элемента коллекции. Когда фактическим параметром типа является ?, он обозначает какой-то неизвестный тип. Любой параметр, который мы передаем в add, должен быть подтипом этого неизвестного типа. Поскольку мы не знаем, какой это тип, мы не можем ничего передать. Единственным исключением является null, который является членом каждого типа.

(выделено мной)

person Michael Myers    schedule 02.02.2009

У mmyers есть правильный ответ, но я просто хотел прокомментировать эту часть вашего вопроса (что звучит как ваше обоснование желания использовать подстановочный знак):

Я думал, что то, как я использовал подстановочный знак, делает ссылочную переменную c типа GenClass, которая будет принимать в качестве параметра любой класс - на месте E у меня будет любой класс. Это просто объявление переменной. Затем я инициализирую его с помощью

Если вы действительно хотите это сделать, вы можете сделать что-то вроде без ошибок компиляции:

GenClass<Object> gc = new GenClass<Object>();
gc.setVar(new ExampleClass());

Но опять же, если вы хотите объявить экземпляр GenClass, который может содержать любой тип, я не уверен, почему вы вообще хотите использовать дженерики - вы можете просто использовать необработанный класс:

GenClass raw = new GenClass();
raw.setVar(new ExampleClass());
raw.setVar("this runs ok");
person matt b    schedule 02.02.2009