为什么这两种比较 Class 对象的结果不同?

Why do these two way to compare Class objects result differently?

我定义了两个类:Employee和Doctor,他们是父子。代码如下:

class Employee {
  // ....
}

class Doctor extends Employee {
  // ....
}

然后我写了一个像这样的主要方法:

public static void main(String[] args) {
        Doctor doctor = new Doctor();
        Employee employee = new Employee();
        System.out.println(doctor.getClass() == new Employee().getClass());  // code 1
        System.out.println(employee.getClass() == Employee.class);  // code 2
        System.out.println(doctor.getClass() == Employee.class); // code 3
    }

但只有 code 1code 2 是正确的,code 3 调用异常:

Error:(33, 46) Java: uncomparable type: java.lang.Class<capture#1 extends Doctor> and java.lang.Class<Employee>

而且我知道我可以使用equals来处理这个问题,但我想知道为什么我不能使用运算符==来比较它们。

您观察到的行为是正确的,但要解释原因,我们需要参考 Java 语言规范。

§15.8.2. Class Literals

The type of C.class, where C is the name of a class, interface, or array type (§4.3), is Class<C>.

§4.3.2. The Class Object 说:

The type of a method invocation expression of getClass is Class<? extends |T|>, where T is the class or interface that was searched for getClass (§15.12.1) and |T| denotes the erasure of T (§4.6).

§15.21.3. Reference Equality Operators == and != 说:

It is a compile-time error if it is impossible to convert the type of either operand to the type of the other by a casting conversion (§5.5). The run-time values of the two operands would necessarily be unequal (ignoring the case where both values are null).

所以,我们可以写下每个表达式的编译时类型:

  • Employee.class 属于 Class<Employee>.
  • 类型
  • Doctor.class 属于 Class<Doctor>.
  • 类型
  • employee.getClass()new Employee().getClass() 都是 Class<? extends Employee> 类型。这意味着 Class 代表 Employee 或其子类,包括 Doctor.
  • doctor.getClass()new Doctor().getClass() 都是 Class<? extends Doctor> 类型。这意味着 Class 代表 Doctor 或其子类,例如Surgeon也许。

现在我们可以解释所有三种行为:

  1. doctor.getClass() == new Employee().getClass()Class<? extends Doctor>Class<? extends Employee> 进行比较。第一种类型可以通过强制转换转换为第二种类型,所以这是允许的。
  2. employee.getClass() == Employee.classClass<? extends Employee>Class<Employee> 进行比较。第二种类型可以通过强制转换转换为第一种类型,所以这是允许的。
  3. doctor.getClass() == Employee.classClass<? extends Doctor>Class<Employee> 进行比较。两种类型都不能通过强制转换转换为另一种类型,因此这是一个编译时错误(不是异常)。

关于 3. 的更多细节,Class<Doctor>Class<Surgeon> 可以满足 Class<? extends Doctor>,但 Class<Employee> 不能满足,因为 Employee不是 Doctor 的子类型。但是,如果在调用 getClass:

之前向上转换 doctor,则可以编写一个类似的表达式,它具有预期的结果
  1. ((Employee) doctor).getClass() == Employee.class与情况2类似,所以允许。

诚然,通过向上转型而不是向下转型来修复类型错误是很不寻常的。