Почему эти два способа сравнения объектов Class приводят к разным результатам? - PullRequest
2 голосов
/ 23 февраля 2020

Я определил два класса: сотрудник и доктор, они отец и сын. Код, подобный следующему:

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 1 и code 2 верны, а code 3 вызывает исключение :

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

и я знаю, что могу использовать equals для решения этой проблемы, но я хочу знать, почему я не могу использовать оператор == для их сравнения.

1 Ответ

1 голос
/ 23 февраля 2020

Вы наблюдаете правильное поведение, но чтобы объяснить, почему, нам нужно обратиться к Java Спецификации языка.

§15.8.2. Литералы класса говорят

Тип C.class, где C - это имя класса, интерфейса или типа массива (§4.3), равно Class<C>.

§ 4.3.2. Класс Object говорит:

Тип выражения вызова метода getClass: Class<? extends |T|>, где T - это класс или интерфейс, в которых был произведен поиск * 1023. * (§15.12.1) и |T| обозначает стирание T (§4.6).

§15.21.3. Операторы справочного равенства == и != говорят:

Ошибка времени компиляции, если невозможно преобразовать тип одного операнда в тип другого с помощью преобразование кастинга (§5.5). Значения времени выполнения двух операндов обязательно будут неравными (игнорируя случай, когда оба значения равны 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.class сравнивает Class<? extends Employee> с Class<Employee>. Второй тип может быть преобразован в первый тип путем преобразования приведения, поэтому это разрешено.
  3. doctor.getClass() == Employee.class сравнивает Class<? extends Doctor> с Class<Employee>. Ни один тип не может быть преобразован в другой с помощью преобразования приведения, так что это ошибка времени компиляции (не исключение).

Если немного подробнее рассказать о 3., Class<? extends Doctor> может быть удовлетворенным Class<Doctor> или Class<Surgeon>, но не Class<Employee>, потому что Employee не является подтипом Doctor. Однако вы можете написать аналогичное выражение, которое даст ожидаемый результат, если вы передадите doctor перед вызовом getClass:

((Employee) doctor).getClass() == Employee.class аналогичен случаю 2., так что это разрешено.

По общему признанию довольно необычно исправлять ошибку типа, используя upcast вместо downcasting.

...