Обобщение Метод Аргумент Поведение - PullRequest
0 голосов
/ 06 января 2019

Рассмотрим обобщенную методическую программу ниже:

class GenericTest {
    int i;
}
public class GenericMethod {

    public <T> List<T> addList(T t, T t1) {
        List<T> list=new ArrayList<T>();
        list.add(t);
        list.add(t1);
        return list;
    }
    public <T> List<T> addList(T t, T t1, List<T> l) {
        List<T> list=new ArrayList<T>();
        list.add(t);
        list.add(t1);
        list.addAll(l);
        return list;
    }
    public static void main(String[] args) {
        GenericMethod gm=new GenericMethod();
        List l=gm.addList(new Integer(42), "java");           //Line 21
        System.out.println(l);
        List<Object> list = new ArrayList<Object>();
        list.add(false);
        List l2 = gm.addList("java", new Integer(42), list);     //Line 25
        System.out.println(l2);
        GenericTest gt = new GenericTest();
        List l3 = gm.addList("java", gt);          //Line 28
        System.out.println(l3);
        List l4 = gm.addList(gm, gt);              //Line 30
        System.out.println(l4);
        List lst = new ArrayList();
        lst.add(new Object());
        List l5 = gm.addList("java", new Integer(42), lst);    //Line 34
        System.out.println(l5);
        System.out.println(l5.get(0));
    }
}

Перейдя по Справочной ссылке Generics Java - Непонятное поведение Я предполагаю,

В строке 21, так как метод вызывается как с String, так и с Integer, он вызывает метод с Object для T. То же самое относится и к строке 25, поскольку List также имеет тип Object, он вызывает метод с Object для T. Вызывает List с другой тип выдаст ошибку. Правильный ли мой вывод?

То, о чем я не могу думать, - это

1) Строка 28, когда переданы String и class,

2) Строка 30, когда пройдены два разных класса,

3) Строка 34, когда List, объявленный без типа, передается в качестве аргумента.

Может кто-нибудь поделиться своими знаниями, чтобы очистить мое понимание. Любая помощь будет оценена! Спасибо!

Ответы [ 2 ]

0 голосов
/ 06 января 2019

В общем, старайтесь избегать объявления List без параметра типа. Даже если вам нужно сделать List<Object>, это более понятно. (и вам, и компилятору)

Строка 25 не будет компилироваться с String, Integer и List<String>, поскольку передача List<String> определенно заставит компилятор предположить, что <T> является String .

List l2 = gm.addList("java", new Integer(42), new ArrayList<Object>()); // compiles

List l2 = gm.addList("java", new Integer(42), new ArrayList<String>()); // does not compile

Вы можете изменить объявление для addList на:

public <T> List<T> addList(T t, T t1, List<? extends T> l) {

Тогда оба примера будут скомпилированы. Параметр type в параметре List больше не является строго T, а скорее T или супертипом T. Компилятор считает, что параметр типа Object, так как Object является супертипом String (и других параметров), код компилируется.

Строка 28, строка 30 и строка 34 возвращают List<Object> относительно компилятора. Единственный общий тип T, который может совместно использоваться параметрами - это Object, поэтому компилятор делает вывод, что T равен Object.

Что касается JVM во время выполнения, нет параметров типа из-за стирания типа , и все они просто List. Параметры типа отсутствуют во время выполнения.

0 голосов
/ 06 января 2019

Список вызовов с другим типом вызовет ошибку. Правильный ли мой вывод?

Да; например, вы не можете пропустить List<String>. Обычно цель вашего второго метода addList неверна. Правильная подпись такова:

public <T> List<T> addList(T t1, T t2, List<? extends T> list)

обратите внимание на синтаксис ? extends. Если все, что вы делаете, это читаете из списка, то нет причин не добавлять это. Список некоторого определенного подтипа T гарантированно содержит Ts. Причина в том, что есть разница: что, если вы хотите ADD к этому списку? Допустим, T равен Number, и вы передаете список Integer. Двойной номер. С List<T> list вы можете вызвать list.add(5.0), и он скомпилируется и запустится, поместив двойные числа в ваш список целых чисел. При List<? extends T> вызов add всегда является ошибкой компилятора (если вы не попытаетесь добавить null, что сработает). Для чтения вы получаете T, независимо от того, звоните ли вы get на List<? extends T> или List<T>, это не имеет значения.

1) Строка 28, когда передается String и class,

Нет, вы передаете экземпляр типа String и экземпляр типа GenericTest. String - это такой же класс, как и GenericTest. Строка не особенная. Это точно такой же сценарий, как ваш вызов в строке 21: вы передаете 2 выражения; один тип объекта X, а другой тип объекта Y. Следовательно, T определяется как наиболее определенный общий тип (возможно, так называемый тип lub: набор типов) между двумя переданными экземплярами. В случае строк 21, Integer и String наиболее конкретный общий тип - это просто Object (если быть техническим, то это тип lub Object & Serializable, но здесь это в основном не имеет значения). В строке 28 это наиболее специфичный общий тип между String и GenericTest, который по-прежнему Object, без разницы.

2) Строка 30, когда пройдены два разных класса,

см. Выше; точно такая же ситуация. Наиболее конкретный тип общего доступа между GenericTest и GenericMethod - Object.

3) Строка 34, когда List, объявленный без типа, передается в качестве аргумента.

Выражение lst в строке 34 имеет тип «raw» ArrayList. когда вы используете необработанные типы, происходят две вещи: [1] компилятор предупредит вас о том, что вы используете необработанные типы, и [2] в значительной степени все универсальные тесты и проверки отключены для любых вызовов, которые включают любые необработанные типы, поэтому компилятор просто допустит это.

Помните, что дженерики - это плод воображения компилятора. Их цель - чтобы компилятор сказал вам, что ваш код не работает. Вот и все.

...