Почему метод java.lang.Class getInterfaces() возвращает класс <?> [], А не класс <? супер T> []?
(Чтобы прояснить вопрос, "T" ссылается на параметр типа, объявленный в классе)
Как пример, просмотрите следующее приложение:
public class TestClass {
interface InterfaceA{}
interface InterfaceB{}
interface InterfaceC{}
class ClassA implements InterfaceA, InterfaceB, InterfaceC{}
public static void main(String[] args){
Class<? super ClassA> superClass1 = ClassA.class;
Class<? super ClassA> superclass2 = InterfaceA.class;
Class<? super ClassA> superclass3 = InterfaceB.class;
Class<? super ClassA> superclass4 = InterfaceC.class;
for(Class<?> clazz : ClassA.class.getInterfaces()){
System.out.println(clazz.getName());
}
}
}
Результат - это то, что можно было бы ожидать:
TestClass$InterfaceA
TestClass$InterfaceB
TestClass$InterfaceC
Итак, на основе вышеприведенного примера мы можем видеть, что компилятор распознает, что InterfaceA выполняет соответствие рамкам подстановки, как и все другие интерфейсы.
Интуитивно я ожидал бы, что следующее будет безопасным, но это не так:
Class<? super ClassA>[] interfaces = ClassA.class.getInterfaces();
Компилятор дает предупреждение, потому что подпись говорит, что возвращает класс; однако javadoc для класса утверждает следующее:
Если этот объект представляет класс, возвращаемое значение представляет собой массив содержащие объекты, представляющие все интерфейсы, реализованные класс.
"Этот объект" относится в нашем случае к ClassA
. Исходя из этого утверждения, если мы вызываем:
ClassA.class.getInterfaces()
то мы логически знаем, что каждый Class<?>
в возвращаемом массиве будет содержать ссылку на супер-тип ClassA
.
В качестве дополнительной точки отсчета:
Из справочника по языку Java, третье издание:
Класс обязательно реализует все интерфейсы, которые его прямые суперклассы и прямые суперинтерфейсы. Этот (несколько) интерфейс Наследование позволяет объектам поддерживать (множественное) общее поведение без совместного использования.
Считая эту и другие части спецификации, я бы подумал, что любой класс или интерфейс, реализованный или расширенный классом T
, попадает в границы <? super T>
.
Редакция:
Было высказано предположение, что нет конкретных причин для моего вопроса. Я приведу пример:
1 import java.util.Map;
2 import java.util.HashMap;
3
4 public class MapWrapper<T> {
5 private final Map<Class<?>, OtherClass<T,?>> map = new HashMap <Class<?>, OtherClass<T,?>>();
6
7 public <W> void addToMap(Class<W> type, OtherClass<T,? super W> otherClass){
8 map.put(type, otherClass);
9 }
10
11 public <W> OtherClass<T,? super W> getOtherClassForAnyInterface(Class<W> type){
12 if(type.getInterfaces()!=null){
13 for(Class<?> interfaceType : type.getInterfaces()){
14 // Here, a cast is necessary. It throws a compiler warning for unchecked operations (rightfully so)
15 OtherClass<T,? super W> otherClass = (OtherClass<T,? super W>)getOtherClassForInterface(interfaceType);
16 if(null!=otherClass){
17 return otherClass;
18 }
19 }
20 }
21 return null;
22 }
23
24
25 public class OtherClass<T,V> {}
26
27 }
Проблема заключается в строке 15. Чтобы получить правильный тип, вам нужно указать <? super W>
. Предупреждение компилятора может быть подавлено, но оправдано ли это? Есть ли какой-то сценарий, в котором это приведение не соответствует действительности?
Ответы
Ответ 1
Вы правы, тип возврата может быть более конкретным.
Однако Class<? super X>
не очень-то очень полезен. Class<?>
достаточно для большинства применений.
Если у нас есть объявление G<T>
, для G<? super X>
полезно, обычно G
должен иметь методы, которые принимают T
. Например, List<T>
имеет add(T)
. Так что List<? super X>
полезно, мы можем называть его add(x)
(x
типа x
)
Class
не имеет таких методов.
У вас есть убедительный прецедент, который вам действительно нужен Class<? super ClassA>
?
Ответ 2
Опять скажем, что у вас есть это объявление универсального метода:
Пример без массива
<T super Integer> void add(T number) // hypothetical! currently illegal in Java
И у вас есть эти объявления переменных:
Integer anInteger
Number aNumber
Object anObject
String aString
Ваше намерение с <T super Integer>
(если оно законно) заключается в том, что оно должно разрешать add(anInteger)
и add(aNumber)
, и, конечно, add(anObject)
, но NOT add(aString)
. Ну, String - это Object, поэтому add(aString)
все равно будет компилироваться.
скопировать из: Переполнение стека - обобщение генериков с ключевым словом "super"
Это помогло мне. Может помочь тоже:)