Предупреждение о непроверенной задаче

Я использую Android Studio 1.1.0.

Это не вызывает никаких предупреждений:

public static class A {
    public Map<Integer, String> getMap() {
        return null;
    }
}

public static class B {
    public void processA(A a) {
        Map<Integer, String> map = a.getMap();
    }
}

Но сделайте A generic:

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

И эта строка:

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

теперь вы получаете предупреждение: "Unchecked assignment: 'java.util.Map to java.util.Map<java.lang.Integer, java.lang.String>'.

Несмотря на то, что подпись getMap полностью не зависит от T, и код недвусмыслен относительно типов, которые содержит Map.

Я знаю, что я могу избавиться от предупреждения, переопределив processA следующим образом:

public <T> void processA(A<T> a) {
    Map<Integer, String> map = a.getMap();
}

Но зачем мне это делать? Что T имеет здесь вообще?

Итак, вопрос в том, почему стирание типа должно не только влиять на T (что понятно - если я передаю экземпляр A, T неизвестен), но и "жестко закодированная" общая подпись типа <Integer, String> в этом случае?

Ответ 1

Во втором случае, когда вы это делаете:

public void processA(A a)

Что вы подразумеваете под A? Означает ли это A<String> или A<List<String>> или что? Возможно, вы не используете ничего, связанное с типом A, но, к сожалению, компилятор этого не знает. Для компилятора просто A является признаком паники.

В вашем случае, потому что вам не нужно знать тип A, вы можете:

public void processA(A<?> a) {
    Map<Integer, String> map = a.getMap();
} 

Наличие типа аргумента A<?> Означает, что вам не нужен тип A и просто укажите wild card. Для вас это означает: любой объект A с любым типом, как это делал бы его общий тип. На самом деле это означает, что вы не знаете тип. Это бесполезно, потому что вы не можете делать что-либо, связанное с A в виде ? может быть практически любым!

Но в соответствии с вашим телом метода, это делает весь смысл в мире использовать A<?> Потому что нет, где в теле вам действительно нужен тип A

Ответ 2

Когда вы хотите принять A<T> любого возможного типа T, но не нуждаетесь в T, это правильно выражается с помощью подстановочного знака и написания A<?>. Это позволит избавиться от предупреждения в вашем коде:

public void processA(A<?> a) {
    Map<Integer, String> map = a.getMap();
}

Использование голого типа A не обрабатывается эквивалентно. Как поясняется в Спецификации языка Java, необработанные типы, подобные этим, не предназначены для использования в новом коде:

Непроверенное преобразование используется для обеспечения плавного взаимодействия старого кода, написанного до введения родовых типов, с библиотеками, которые подверглись преобразованию, чтобы использовать универсальность (процесс, который мы называем генерацией). В таких обстоятельствах (в частности, клиенты Framework Collections Framework в java.util), устаревший код использует необработанные типы (например, Collection вместо Collection <String>). Выражения типов raw передаются в качестве аргументов библиотечным методам, которые используют параметризованные версии тех же типов, что и типы соответствующих формальных параметров.

Такие вызовы не могут быть показаны как статически безопасные в системе типов с использованием дженериков. Отклонение таких вызовов приведет к недействительности больших тел существующего кода и не позволит им использовать более новые версии библиотек. Это, в свою очередь, препятствовало бы тому, чтобы продавцы библиотек воспользовались преимуществами родословной. Чтобы предотвратить такой нежелательный поворот событий, необработанный тип может быть преобразован в произвольный вызов объявления общего типа, к которому относится исходный тип. Хотя преобразование является необоснованным, оно допускается как уступка практичности. В таких случаях выдается непроверенное предупреждение.