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.
The behaviour you observe is correct, but to explain why, we need to refer to the Java Language Specification.
The type of
C.class
, whereC
is the name of a class, interface, or array type (§4.3), isClass<C>
.
§4.3.2. The Class Object
says:
The type of a method invocation expression of
getClass
isClass<? extends |T|>
, whereT
is the class or interface that was searched forgetClass
(§15.12.1) and|T|
denotes the erasure ofT
(§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:
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.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.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
:
((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.