为什么java.lang.Class的getInterfaces()方法返回Class<?>[]而不是Class<? super T>[]?

13
为了澄清问题,“T”是在Class中声明的类型参数。
举个例子,请查看以下应用程序:
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();

编译器会发出警告,因为函数签名表明它返回一个Class对象;然而,Class类的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>才能得到正确的类型。编译器警告可以被忽略,但这样做是否合理呢?是否存在某些情况下这种类型转换是不正确的呢?
2个回答

5
你说得没错,返回类型可以更加具体化。
然而,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>

3
Class<T>可能没有实际使用T的任何超类的方法,但作为类型本身,它确实声称提供有关超类型的信息。如果您查看Class的getSuperclass()方法,其返回类型是Class<? super T>。鉴于这一点,对我而言,getInterfaces()返回类似的类型似乎更为合适。如果他们像您所建议的那样考虑,即Class永远不会使用通过允许有界通配符可访问的那些方法,为什么Class会提供一个被指定为<? super T>的超类呢? - sager
这可能是API设计师的错误。我认为无论如何都无关紧要。 - irreputable
我已经添加了一个使用类的具体案例。现在它已经在任务中了。 - sager

0

假设您有以下通用方法声明:

非数组示例

<T super Integer> void add(T number) // 假设!目前在Java中是非法的

并且您有以下变量声明:

Integer anInteger Number aNumber Object anObject String aString

如果<T super Integer>(如果合法)是您的意图,则应允许add(anInteger)add(aNumber)和当然add(anObject),但不允许add(aString)。好吧,String是一个Object,所以add(aString)仍然会编译。

摘自:Stack Overflow - Bounding generics with 'super' keyword

它对我很有帮助。也许对你有帮助 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接