比较Class类型时出现编译错误:不可比较类型的原因探究
代码示例
class A {} class B extends A {} @Test public void test4() { A a = new A(); B b = new B(); Class<? extends A> aClass = a.getClass(); Class<? extends B> bClass = b.getClass(); System.out.println(aClass == bClass);// #1 false System.out.println(aClass == A.class);// #2 true System.out.println(aClass == B.class);// #3 false System.out.println(bClass == A.class);// #4 error:java: non comparable type: java.lang.Class<capture#1, ? extends Test.B> and java.lang.Class<Test.A> System.out.println(bClass == B.class);// #5 true }
问题描述
我对#4处的编译错误感到困惑:调试时a.getClass()和A.class确实是同一个对象,原本以为#3和#4逻辑等价,不应出现错误,猜测可能和类型擦除有关但了解有限,想问出现该错误的原因是什么?
原因解析
这个错误和泛型的类型兼容性检查直接相关,核心是编译器对Class泛型类型的子类型规则判断:
getClass()的返回类型规则
根据Java规范,x.getClass()的返回类型是Class<? extends |X|>,其中|X|是变量x编译时静态类型的擦除。这里:a的静态类型是A,所以a.getClass()返回Class<? extends A>;b的静态类型是B,所以b.getClass()返回Class<? extends B>。
#3能编译的原因
aClass的类型是Class<? extends A>,B.class的类型是Class<B>。由于B extends A,而Class的泛型是协变的——即如果X extends Y,那么Class<X>可以被视为Class<? extends Y>的子类型。因此Class<B>和Class<? extends A>是兼容的,编译器允许进行==引用比较。#4编译错误的原因
bClass的类型是Class<? extends B>,A.class的类型是Class<A>。但A是B的父类,并非B的子类,所以Class<A>不属于Class<? extends B>的范畴(? extends B仅代表B或它的子类的Class类型)。编译器在编译阶段会严格检查类型兼容性,认为这两个类型没有可比较的子类型关系,因此抛出“不可比较类型”的错误。
简单说:#3是“子类Class类型”和“父类范围的Class通配符类型”比较,符合协变规则;#4是“父类Class类型”和“子类范围的Class通配符类型”比较,完全不兼容,编译器直接拦截。
内容的提问来源于stack exchange,提问作者cli ash

