2013-04-09 2 views
2

Может кто-то объяснить это поведение мне:Java Generic Type Inference Странное поведение?

Примечания: Это T никогда не используется в SomeThingGeneric

public static class SomeThingGeneric<T> { 
     public List<String> getSomeList() { 
      return null; 
     } 
    } 

final SomeThingGeneric<Object> someThingGenericObject = new SomeThingGeneric<Object>(); 
final SomeThingGeneric<?> someThingGenericWildcard = new SomeThingGeneric<Object>(); 
final SomeThingGeneric someThingGenericRaw   = new SomeThingGeneric<Object>(); 

for (final String s : someThingGenericObject.getSomeList()) { } // 1 - compiles 
for (final String s : someThingGenericWildcard.getSomeList()) { } // 2 - compiles 
for (final String s : someThingGenericRaw.getSomeList()) { }  // 3 - does not compile! 

(1) и (2) компилирует, но (3) не с следующим сообщением:

incompatible types 
found : java.lang.Object 
required: java.lang.String 

Если кому-то нужен полный код, here it is. Я проверил это как в Java 5 и 6.

+2

Возможный дубликат [Почему этот класс ведет себя по-другому, если я не предлагаю общий тип?] (Http://stackoverflow.com/questions/15735235/why-does-this-class-behave-differently-when -i-dont-supply-a-generic-type) –

+0

Тем не менее, вопрос и ответ применимы здесь. (В частности, ответ: «это было сделано для обратной совместимости, и это не должно быть проблемой, потому что вы никогда не должны использовать необработанные типы в новом коде».) –

+0

@wrick 'Ищите ответный рисунок из надежных и/или официальные источники. '- что еще, помимо цитат из JLS, вы хотели бы увидеть? –

ответ

4

Ну, это интересный вопрос, несмотря на downvotes. Я считаю, что ответ на этот вопрос лежит в этой части JLS:

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

Эффективно, ваш метод public List<String> getSomeList получает эффективную подпись public List getSomeList, в сценарии, где доступ к ним с помощью сырого типа. И как таковой, итератор списка для результирующего списка затем возвращает объекты вместо строк.

+0

Один вопрос - почему!?! – pathikrit

+1

Нет особо веских оснований, которые я могу придумать или найти в любом тексте. Если поля/методы были введены в общий параметр класса, который сделал бы (очевидный) смысл. Может быть, для обеспечения совместимости с устаревшим кодом. Тем не менее, поведение документировано в JLS, fwiw. – Perception

3

Код может быть разбита на следующие SSCCE:

public static class SomeThingGeneric<T> { 
    public List<String> getSomeList() { 
     return null; 
    } 
} 

public static void main(final String[] args) { 
    final SomeThingGeneric someThingGenericRaw = new SomeThingGeneric<Object>(); 
    for (final String s : someThingGenericRaw.getSomeList()) { }  // SAD compiler. WTF? 
} 

Это приводит к ошибке компилятора Type mismatch: cannot convert from element type Object to String.

Вопрос в следующем: почему возникает эта ошибка? T не используется нигде в SomeThingGeneric, и особенно в подписи метода getSomeList(). Поэтому, когда мы звоним getSomeList(), мы должны получить List<String>. Однако мы, очевидно, получаем необработанный тип List.


Причина - тип стирания. Из JLS:

Тип стирание также отображает подпись (§8.4.2) из ​​конструктора или методы к подписи, который не имеет ни одного параметризованных типов или типа переменных.

И, the definition of a raw type говорит:

Для облегчения взаимодействия с необщим унаследованным кодом, можно использовать в качестве типа стирания (§4.6) параметризированного типа (§ 4.5) или стирание типа массива (§10.1), тип элемента которого является параметризованным типом. Такой тип называется необработанным типом.

Таким образом, при использовании сырого типа, это универсальный тип с типом стиранием применяется. При применении стирания стилей все сигнатуры методов сопоставляются типам без параметров типа или переменных типа. Следовательно, способ List<String> getSomeList() из примера становится List getSomeList(), что приводит к ошибке показанного компилятора.

+0

Но обратите внимание, что я не использую T нигде в SomeThingGeneric – pathikrit

+1

А, хорошо, - сначала не узнал об этом ... давайте посмотрим ... (кстати, именно поэтому полезно описать проблему a littlebit в вопросе;)) –

+1

Я не спустил;) Вы проверили с более ранней версией компилятора (1.6/1.5)? –

0

По моему мнению, это ошибка. Потому что он должен работать. Подпись метода не использует общий тип T. Поэтому он должен скомпилировать.

Отметьте весь класс как стирание, не компилируйте очень четко определенные общие методы, это не хорошо.

Просьба сообщить об этом.

+0

Это не ошибка. Он хорошо документирован в JLS. –

+0

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

+0

Логика: не используйте необработанные типы. Они предоставляются только по соображениям совместимости. См. Ссылку от @Louis в своем комментарии. –

3

Ответ: Когда вы создаете типичный тип как необработанный тип, все его экземпляры экземпляра удаляются, оставляя необработанные типы везде (даже если они не относятся к параметрам типа, таким как T в вашем Q). т. е. необработанные типы на 100% «без генериков».

Причина: обратная совместимость с предварительным кодом.