Предупреждение о непроверенной задаче
Я использую 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 передаются в качестве аргументов библиотечным методам, которые используют параметризованные версии тех же типов, что и типы соответствующих формальных параметров.
Такие вызовы не могут быть показаны как статически безопасные в системе типов с использованием дженериков. Отклонение таких вызовов приведет к недействительности больших тел существующего кода и не позволит им использовать более новые версии библиотек. Это, в свою очередь, препятствовало бы тому, чтобы продавцы библиотек воспользовались преимуществами родословной. Чтобы предотвратить такой нежелательный поворот событий, необработанный тип может быть преобразован в произвольный вызов объявления общего типа, к которому относится исходный тип. Хотя преобразование является необоснованным, оно допускается как уступка практичности. В таких случаях выдается непроверенное предупреждение.