Стирание типов и наследование: объект List‹Number› при доступе из подкласса рассматривается как необработанный список?

Вопрос:

Объект параметризованного класса при доступе из подкласса рассматривается как необобщенный. Как мне преодолеть это?

Подробности:

Рассмотрим базовый класс:

public class MyBaseClass {
    protected List<Number> numbers = new ArrayList<Number>();

    public MyBaseClass(){
        //...
    }
}

Теперь давайте расширим этот класс:

public class MyDerivedClass extends MyBaseClass {

    public void addSomething() {
        numbers.add(25.0f); //Warning about type-safety here
    }
}

В производном классе объект чисел рассматривается как необработанный тип. Предупреждение:

Безопасность типов: метод add(Object) относится к необработанному типу List. Ссылки на общий тип List должны быть параметризованы.

Если я попытаюсь добавить параметр типа самостоятельно, я получу ошибку компиляции:

        numbers.<Number>add(25.0f); //Error here

Я вижу ошибку:

Метод add(Number) типа List не является универсальным; его нельзя параметризовать аргументами

Однако, если я помещу оба класса в один и тот же исходный файл Java, эта проблема исчезнет. Во-первых, нет предупреждения. Это был образец для иллюстрации проблемы.

В моем реальном приложении я хочу иметь возможность получать элементы из List в производном классе и выполнять над ними специфичные для класса операции без необходимости сначала приводить полученный объект к соответствующему типу.

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


РЕДАКТИРОВАТЬ:

Я использую Eclipse 3.7.2 и Oracle JDK 1.6.0_30 на Fedora Core 16. Соответствие компилятора установлено на 1.6 с настройками соответствия по умолчанию. Как я уже упоминал, если я помещаю оба класса в один и тот же файл .java, я не вижу предупреждения. Видно, только если я помещаю их в разные файлы.

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

Но мне все еще любопытно, почему я вижу предупреждение. Вот обходной путь:

В MyBaseClass:

    protected void addNumberToList(Number num){
        numbers.add(num);
    }

а затем в производном классе:

    public void addSomething() {
        addNumberToList(25.0f);
    }

Точно так же все операции, которые напрямую манипулируют объектом List, могут быть в базовом классе.


person curioustechizen    schedule 21.06.2012    source источник
comment
Какой компилятор, мой так себя не ведет?   -  person dacwe    schedule 21.06.2012
comment
Здесь же я попробовал это, и для меня нет предупреждения.   -  person Subin Sebastian    schedule 21.06.2012
comment
Я озадачен, приведенный выше код компилируется без ошибок и предупреждений на моей машине, как и должно быть. Я использую JDK 7 Update 4. Какую версию компилятора вы используете?   -  person alexraasch    schedule 21.06.2012
comment
Это действительно озадачивает! Я отредактировал вопрос, добавив сведения о компиляторе, настройках соответствия и других деталях среды.   -  person curioustechizen    schedule 21.06.2012


Ответы (2)


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

public class MyBaseClass<X> {
    protected List<Number> numbers = new ArrayList<Number>();

    public List<Number> getNumbers() {
        return numbers;
    }
}

public class MyDerivedClass extends MyBaseClass {
    public void addSomething() {
        numbers.add(25.0f);
    }
}

JLS §4.8 говорит, что доступ к членам будет рассматриваться как для необработанных типов. Это означает, что numbers обрабатывается как необработанное, отсюда и предупреждение. Решение состоит в параметризации супертипов:

public class MyDerivedClass extends MyBaseClass<TypeArgumentsHere>
person Ben Schulz    schedule 21.06.2012
comment
Отличный ответ. +1 за ссылку на раздел 4.8 JLS. Вы указали на спецификации для SE7; Я просмотрел то же самое для SE5 и там то же самое. Однако я не вижу предупреждения, если помещаю оба класса в один и тот же файл. Кроме того, другие ответы и комментарии здесь по этому вопросу заявляют, что они не видят предупреждения. Как бы вы это объяснили? - person curioustechizen; 21.06.2012
comment
@curioustechize Код, который вы разместили в своем вопросе, в порядке. Это тип звука и без предупреждений. Я полагаю, что большинство плакатов попробовали это и не увидели никаких предупреждений. Если бы они попробовали код, который я разместил, они бы это сделали или, по крайней мере, должны были бы. - person Ben Schulz; 21.06.2012
comment
Я думаю, что здесь есть недоразумение. Я вижу предупреждения с кодом, опубликованным мной в исходном вопросе. Это то, что пробовали другие плакаты и сообщали, что они работают нормально без предупреждений (как и должно быть в соответствии с вашим объяснением). - person curioustechizen; 21.06.2012
comment
@curioustechizen Все, что я могу вам сказать, это то, что - при условии правильного компилятора и отсутствия манипуляций с байтовым кодом - единственный способ получить это предупреждение - это если супертип, к которому принадлежит numbers, является необработанным. - person Ben Schulz; 21.06.2012

Я использую Eclipse, и ваш код работает хорошо, без предупреждений или ошибок. Но я думаю, что вы должны предоставить метод protected/public вместо того, чтобы выставлять переменную экземпляра.

public class MyBaseClass {
    protected List<Number> numbers = new ArrayList<Number>();

    public List<Number> getNumbers() {
        return numbers;
    }
}

public class MyDerivedClass extends MyBaseClass {

    public void addSomething() {
        getNumbers().add(25.0f);
    }
}
person Genzer    schedule 21.06.2012