Подтвердить что ты не робот

Почему этот общий код Java не компилируется?

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

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
4b9b3361

Ответ 1

Получил это. На самом деле это не ошибка, как это ни странно.

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

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

Итак, хотя подпись типа метода не использует параметры типа самого класса, введите erasure kicks in и подпись станет эффективно

public Map getMap()

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

EDIT: Этот код:

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("");

Ответ 2

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

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

Ответ 3

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

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

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

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

Ответ 4

После компиляции стираются стили.

Когда вы выполните:

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

вы вынуждаете актерский состав из Map to Map < String, String > , и поэтому это предупреждение непроверено. Однако после этого вы можете сделать:

String s2 = map.get("");

потому что map имеет тип Map < String, String > .

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

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

вы не выполняете функцию fail.getMap() для чего-либо, поэтому он считается явно Map, а не Map < String, String > .

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

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

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