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

HashMap :

I've defined two classes : Employee and Doctor, They're father and son. The code like this:

class Employee {
  // ....
}

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

And then I wrote a main method like this:

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
    }

but only code 1 and code 2 is correct and code 3 calls exception :

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

and I know I can use equals to deal with this problem, but I want to know why I can't use operator == to compare them.

kaya3 :

The behaviour you observe is correct, but to explain why, we need to refer to the Java Language Specification.

§15.8.2. Class Literals says

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 says:

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 != says:

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).

So, we can write down the compile-time types of each expression:

  • Employee.class is of type Class<Employee>.
  • Doctor.class is of type Class<Doctor>.
  • employee.getClass() and new Employee().getClass() are both of type Class<? extends Employee>. This means a Class representing either Employee or a subclass of it, including Doctor.
  • doctor.getClass() and new Doctor().getClass() are both of type Class<? extends Doctor>. This means a Class representing either Doctor or a subclass of it, e.g. Surgeon perhaps.

Now we can explain all three behaviours:

  1. doctor.getClass() == new Employee().getClass() compares a Class<? extends Doctor> with Class<? extends Employee>. The first type can be converted to the second type by a casting conversion, so this is allowed.
  2. employee.getClass() == Employee.class compares a Class<? extends Employee> with a Class<Employee>. The second type can be converted to the first type by a casting conversion, so this is allowed.
  3. doctor.getClass() == Employee.class compares a Class<? extends Doctor> with a Class<Employee>. Neither type can be converted to the other by a casting conversion, so this is a compile-time error (not an exception).

Going into bit more detail on 3., a Class<? extends Doctor> could be satisfied by Class<Doctor> or Class<Surgeon>, but not by Class<Employee> because Employee is not a subtype of Doctor. However, you could write a similar expression which has the intended result if you upcast doctor before calling getClass:

  1. ((Employee) doctor).getClass() == Employee.class is similar to case 2., so it is allowed.

It is admittedly quite unusual to fix a type error by upcasting instead of downcasting.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=9468&siteId=1