Почему компилятор Java запускает предупреждение "unchecked cast" при выдаче Class<? extends Object>
Class<? extends Object>
до Class<Object>
? Как может этот бросок, возможно, потерпеть неудачу?
Такой листинг просто небезопасен вообще, и тот факт, что он безопасен для Class
не может быть обнаружен. Представьте себе такой класс:
// Hypothetical!
class MyClass<T> extends Class<T> {
public T someInstance;
}
Теперь, если вопрос был разрешен, вы можете сделать что-то вроде
MyClass<Integer> myClassWithInteger = new MyClass<Integer>();
myClassWithInteger.someInstance = someInteger;
// This is valid
MyClass<? extends Object> myClassWithWildcard = myClassWithInteger;
// This is NOT valid, but let assume it was:
MyClass<Object> myClassWithObject = myClassWithWildcard;
// This would be valid, because 'someInstance' was of type 'Object' here
myClassWithObject.someInstance = "someString";
// This is valid. But through the unchecked cast, we silently
// sneaked in an STRING as this instance. So this would fail:
Integer i = myClassWithInteger.someInstance;
Вы спросили в комментарии:
Вы говорите, что авторы языка хотели избежать специального лечения для таких безопасных классов, как Class, поэтому они запрещают это по всем направлениям?
Да. Решающим моментом является то, что вопрос о том, безопасен ли такой бросок или нет, зависит от семантики класса. Безопасность не может быть обнаружена синтаксически.
Это действительно не может закончиться. Но это общее предупреждение для повышения, независимо от его параметра.
Вместо этого вы можете использовать:
Class<? extends Object> clazz = String.class;
Class<?> clazzObject = clazz; // Does not generate Warning.
<? extends Object>
и <?>
означают одно и то же, поэтому ваш пример не демонстрирует ничего особенного :)
Если вы не хотите обеспечить определенный класс интерфейса, используя дженерики, вы должны просто использовать "?" вместо "? extends Object". Компилятор принимает объект для каждого неограниченного общего параметра во время компиляции.
Это не говорит, что это незаконно, он говорит, что это небезопасно и может привести к ошибкам во время выполнения.
Рассмотрим следующий код:
List<Integer> li = new ArrayList<Integer>();
List<? extends Number> lf = li;
List<Number> ln = (List<Number>)lf;
ln.add(new Float(1.1));
Integer value = li.get(0);
Это не удастся на последней строке, потому что мы проплываем в Float в этом списке целых чисел через этот небезопасный случай.
Class<Object>
не является родителем, например,Class<String>
.String
расширяетObject
. Я понимаю, что компилятор хотел бы запретить мне приведениеList<String>
кList<Object>
потому что тогда мне будет позволено вставлять не-строки в список, но в приведенииClass
нет опасности. Вы говорите, что авторы языка хотели избежать особого подхода к безопасным классам, таким какClass
поэтому они запрещают это по всем направлениям?