Поведение ключевого слова this с полиморфизмом и привязкой во время выполнения - PullRequest
0 голосов
/ 02 февраля 2019

Я имею следующую полиморфную иерархию классов и хотел бы понять, как оценивается ключевое слово this при использовании динамического связывания.

public class A {
  void f(C c) {}
}

public class B extends A {
  void f(C c) {
    System.out.println("B.f(C)");
    c.f(this);
    C.g(this);
  }
}

public class C {
  static void g(A a) { System.out.println("C.g(A)"); }
  static void g(B b) { System.out.println("C.g(B)"); }
  void f(A a) { System.out.println("C.f(A)"); }
  // void f(B b) { System.out.println("C.f(B)"); }
}

public class D extends C {
  void f(A a) { System.out.println("D.f(A)"); }
  void f(B b) { System.out.println("D.f(B)"); }
}

public static void main(String[] args) {
  A a = new B();
  C c = new D();
  a.f(c);
}

С закомментированным методом C.f(B b) метод main выводит на печать:

B.f(C)
D.f(A)
C.g(B)

Почему ключевое слово this в методе B.f(C c) оценивается в A при вызове нестатической функции f и в B при вызове статической функции g?

Кроме того, я заметил, что если я раскомментирую метод C.f(B b), результат изменится на:

B.f(C)
D.f(B)
C.g(B)

Почему в этом случае тип ключевого слова thisоценивается в B для нестатического и статического метода?

1 Ответ

0 голосов
/ 02 февраля 2019

Есть два шага при выборе метода для вызова.Один шаг (какая перегрузка?) Происходит во время компиляции, другой шаг (какая реализация?) Происходит во время выполнения.


C.f(B b) закомментированный случай

Компилятор выполнитпервый шаг, решив, какую перегрузку вызвать.Существует только одна перегрузка c.f:

void f(A a) { System.out.println("C.f(A)"); }

, поэтому компилятор выбирает это.this имеет тип времени компиляции B, поэтому его можно неявно преобразовать в A.

Почему компилятор не видит метод f(B b) в D?Поскольку компилятор не знает, что c является на самом деле экземпляром D, он будет выглядеть только в классе C, поскольку c имеет тип времени компиляции C.

Во время выполнения обнаружено, что существует две реализации f(A a) - одна в классе C и одна в классе D.Поскольку среда выполнения знает, что c является экземпляром D, будет вызван D.f(A a).

C.f(B b) без комментариев

На этот раз компилятор обнаружит две перегрузки c.f для вызова:

void f (A a) {System.out.println ("Cf (A)");} void f (B b) {System.out.println ("Cf (B)");}

Компилятор выберет метод с наиболее специфическими параметрами.«Наиболее конкретный» означает, что тип является самым дальним из дерева наследования.Это означает, что будет выбран f(B b).

Во время выполнения реализация в D будет вызвана по той же причине, что и в первом случае.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...