Почему этот общий код Java не скомпилируется? - PullRequest
33 голосов
/ 19 марта 2009

В этом упрощенном примере у меня есть общий класс и метод, который возвращает карту независимо от параметра типа. Почему компилятор стирает типы на карте, когда я не указываю тип для содержащего класса?

import java.util.Map;

public class MyClass<T>
{
    public Map<String, String> getMap()
    {   
        return null;
    }

    public void test()
    {   
        MyClass<Object> success = new MyClass<Object>();
        String s = success.getMap().get("");

        MyClass unchecked = new MyClass();
        Map<String, String> map = unchecked.getMap();  // Unchecked warning, why?
        String s2 = map.get("");

        MyClass fail = new MyClass();
        String s3 = fail.getMap().get("");  // Compiler error, why?
    }
}

Я получаю эту ошибку компилятора.

MyClass.java:20: incompatible types
found   : java.lang.Object
required: java.lang.String
                String s3 = fail.getMap().get("");  // Compiler error

Ответы [ 4 ]

35 голосов
/ 19 марта 2009

Понял. Это на самом деле не ошибка, как ни странно.

С раздел 4.8 (необработанные типы) JLS :

Тип конструктора (§8.8), метод экземпляра (§8.8, §9.4) или нестатическое поле (§8.3) M необработанного тип C, который не наследуется от его суперклассы или суперинтерфейсы является стирание своего типа в общем декларация, соответствующая C. тип статического члена необработанного типа C такой же, как его тип в общая декларация, соответствующая C.

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

public Map getMap()

Другими словами, я думаю, что вы можете представить, что необработанный тип является тем же API, что и универсальный тип, но со всеми <X> битами, удаленными из везде (в API, а не в реализации).

РЕДАКТИРОВАТЬ: Этот код:

MyClass unchecked = new MyClass();
Map<String, String> map = unchecked.getMap();  // Unchecked warning, why?
String s2 = map.get("");

компилируется, потому что существует неявное, но неконтролируемое преобразование из необработанного типа Map в Map<String, String>. Вы можете получить тот же эффект, сделав явное преобразование (которое ничего не делает во время выполнения) в последнем случае:

// Compiles, but with an unchecked warning
String x = ((Map<String, String>)fail.getMap()).get("");
3 голосов
/ 19 марта 2009

Очень интересный вопрос и очень интересный ответ от Джона Скита.

Я просто хочу добавить кое-что о глупости или не глупости этого поведения компилятора Java.

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

Мне это не кажется таким глупым.

3 голосов
/ 19 марта 2009

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

Измените тип fail на MyClass<?>, тогда он будет прекрасно компилироваться.

1 голос
/ 19 марта 2009

Общие типы стираются после компиляции.

Когда вы делаете:

Map<String, String> map = unchecked.getMap();

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

String s2 = map.get("");

потому что карта имеет тип Map .

Однако, когда вы делаете

String s3 = fail.getMap().get(""); 

вы ни к чему не приводите fail.getMap (), поэтому он считается просто Map, а не Map .

Что вы должны сделать в последнем, это что-то вроде:

String s3 = ((Map<String, String>fail.getMap()).get("");

, который все равно выдаст предупреждение, но все равно будет работать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...