Вопрос по generics, generic-method, java – Присвоение списка целых чисел списку строк

37

Я изучал Generics в Java, и я подошел к очень интересному фрагменту кода. Я знаю, что в Java запрещается добавлять список одного типа в другой.

List<Integer> integerList = new ArrayList<Integer>();
List<String> stringList=integerList;

Итак, во второй строке я получаю сообщение об ошибке компиляции.
Но если я создаю общий метод внутри класса, как этот,

class  GenericClass <E>{
    void genericFunction(List<String> stringList) {
        stringList.add("foo");
    }
    // some other code
}

И в главном классе вызвать метод со списком Integer Я не получаю никакой ошибки.

public class Main {
  public static void main(String args[]) {

     GenericClass genericClass=new GenericClass();
     List<Integer> integerList= new ArrayList<Integer>();
     integerList.add(100);
     genericClass.genericFunction(integerList);
     System.out.println(integerList.get(0));
     System.out.println(integerList.get(1));
  }
}

Выход
100
Foo

Почему я не получаю никакой ошибки?

@TheLostMind Да, но что странно, это то, чтоgenericFunction() ожидаетList<String>неList<E>и, тем не менее, давая емуList<Integer> работает, тогда как еслиGenericClass не было общего, это не будет работать. Florent Bayle
Вы получаете предупреждения, возможно о необработанных типах? chrylis
@ Chrylis - это ответ. Он использует сырые типы. Компилятору все равно, что вы положите. :) TheLostMind
GenericClass genericClass использует необработанный тип и, следовательно, общие проверки типов наgenericClass явно отключены. ПытатьсяGenericClass<?> genericClass и вы должны получить ошибку. Thomas

Ваш Ответ

5   ответов
0
class  GenericClass <E>{
    void genericFunction(List<String> stringList) {
        stringList.add("foo");
    }
    // some other code
}

когда вы пишете сразу после имени класса. Он указывает параметры типа. Это универсальная функциональность. Здесь вводится переменная типа E, которая может использоваться в любом месте внутри класса. Переменная типа может быть любым указанным вами не примитивным типом: любым типом класса, любым типом интерфейса, любым типом массива или даже переменной другого типа.

23

Вы не получаете никакой ошибки времени компиляции, потому что с помощьюGenericClass<E> в грубом виде:

GenericClass genericClass = new GenericClass();,

вы практически говорите компилятору отключить проверки общего типа, потому что вам все равно.

Итак :

void genericFunction(List<String> stringList)

становится

void genericFunction(List stringList) для компилятора.

Вы можете попробовать следующее:GenericClass<?> genericClass , и вы сразу заметите, что компилятору стало известно о неправильном использовании обобщений, и он покажет вам ошибку:

The method genericFunction(List<String>) in the type GenericClass<capture#1-of ?> is not applicable for the arguments (List<Integer>)

Также, если вы попытаетесь получить класс объекта 2-й позиции во время выполнения:

System.out.println(integerList.get(1).getClass());

вы получите ошибку:java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer.

19

Вы смешали общий ссырой тип, Он будет хорошо скомпилирован, но во время выполнения может не получиться, потому что общая информация теряется во время выполнения.

Универсальный должен использоваться для отслеживания таких ошибок во время компиляции.

Это лучше объяснить вЧто такое необработанный тип и почему мы не должны его использовать? в деталях.

Предупреждение: Тип безопасности: методgenericFunction(List) принадлежит к необработанному типуGenericClass, Ссылки на универсальный типGenericClass<E> должен быть параметризован.

Если у вас есть два метода с одним и тем же именем с другим универсальным типом List, это приводит к ошибке времени компиляции. Компилятор не может разрешить тип Generic в случае аргументов метода, которые могут быть подтверждены приведенным ниже примером кода.

Пример кода: (ошибка компилятора - недопустимый перегруженный метод)

void genericFunction(List<String> stringList){...}
void genericFunction(List<Integer> stringList){...}

Сделайте некоторые изменения и попробуйте снова:

class  GenericClass <E>{
    void genericFunction(List<E> stringList) {
        ...
    }
    // some other code
}

...

GenericClass<String> genericClass=new GenericClass<String>(); // Genreric object
List<Integer> integerList= new ArrayList<Integer>();
integerList.add(100);
genericClass.genericFunction(integerList); // compile time error

Создавайте методы таким способом

class GenericClass<E> {
    private List<E> list = new ArrayList<E>();

    public void addAll(List<E> newList) {
        list.addAll(newList);
    }

    public void add(E e) {
        list.add(e);
    }

    public E get(int index) {
        return list.get(index);
    }
    // some other code
}
12

Это происходит потому, что (довольно удивительно для меня) вы отключили проверку универсального типа длявсе GenericClass учебный класс.

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

GenericClass genericClass = new GenericClass();

И, соответственно, из-за этого ваш следующий код:

class GenericClass<E> {
    void genericFunction(List<String> stringList) {
        stringList.add("foo");
    }
    // some other code
}

получил уточнение:

class GenericClass {
    void genericFunction(List stringList) {
        stringList.add("foo");
    }
    // some other code
}

Обратите внимание, чтоList также стал сырым типом, что довольно удивительно для меня.

Вы можете найти полный ответ здесь, ссылаясь на JLS:https://stackoverflow.com/a/662257/2057294 как объяснил Джон Скит.

Я думаю, что это справедливо (хотя и не то, что вы ожидаете), потому что, если вы решите использовать необработанные типы, предполагается, что вы используете Java 4 или ниже и вообще не имеете доступа к генерикам, так что может также не предоставлять их для методов, не включающих универсальный тип из класса, который был удален.

+1 за «Вы отключили проверку универсального типа для всего класса GenericClass». Я должен признать, я не знал и не ожидал этого. Axel
6

Чтобы добавить к другим ответам.

Действительно, вы смешиваете необработанный (непараметризованный) тип с параметризованными типами, что приводит к потере типа и, по-видимому, правильному аргументу, передаваемомуgenericFunction когда проверки безопасности типа не должны позволять вам.

Вопрос остается наЗачем List<String> тип теряется в непараметризованномGenericClass объект. Причина, по которой компилятор "отключает" проверку типов в вашем случае, заключается встирание типа механизм, который говорит (простыми словами) ваш необработанный объект принадлежит классу со следующим содержанием:

class  GenericClass {
    void genericFunction(List stringList) {
        stringList.add("foo");
    }
    // some other code
}

Который, как вы видите, не делает никаких проверок типовчто бы ни по содержимому передаваемого списка (стирает параметры типавезде). Более того, стирание типа также стирает ваш параметризованный тип изintegerList переменная, что делает его полностью пригодным в качестве аргумента для вашегоgenericFunction метод.

Поэтому, как отмечали другие, всегда лучше помогать Java поддерживать свои безопасные механизмы проверки типов:

     GenericClass<?> genericClass=new GenericClass<Integer>();
     List<Integer> integerList= new ArrayList<Integer>();
     integerList.add(100);
     // no-no here
     genericClass.genericFunction(integerList);
     System.out.println(integerList.get(0));
     System.out.println(integerList.get(1));

Но с другой стороны, если вы это сделаете, компилятор сделает свою работу и сделает из вас базуку.

Подробнее о типе стиранияВот, Это хорошая функция, которая позволяет выполнять проверку универсального типа, сохраняя при этом обратную совместимость с предварительно созданными версиями Java.

Похожие вопросы