Страницы

Поиск по вопросам

пятница, 20 декабря 2019 г.

Java - обращение к полю подкласса, если его экземпляр присвоен ссылке на супер-класс и поля имеют одинаковые имена и дефолтный модификатор доступа

#java #ооп #полиморфизм


Изучая наследование и полиморфизм в java наткнулся на такой пример:

class A {
 int a = 5;
 String doA() {
  return“ a1“;
 }
 protected static String doA2() {
  return“ a2“;
 }
}
class B extends A {
 int a = 7;
 String doA() {
  return“ b1“;
 }
 public static String doA2() {
  return“ b2“;
 }
 void go() {
  A myA = new B();
  System.out.print(myA.doA() + myA.doA2() + myA.a);
 }
 public static void main(String[] args) {
  new B().go();
 }
}


Результат выполнения программы: "b1 a2 5"


b1 - тут понятно. фактический тип класса является B, через динамическое связывание
компилятор вызывает метод doA(), определенный в классе B.
a2 - метод doA2() определен статическим, соответственно происходит ранее связывание,
компилятор вызывает метод класса A, а не экземпляра
5 - вот тут-то для меня и происходит магия. почему не 7? переменная не static и не
final? фактический тип класса B

    


Ответы

Ответ 1



A myA = new B(); ^ Потому что поля в Java не являются полиморфными, соответственно используется класс указателя, в данном случае это класс A. По поводу методов: в Java все методы являются виртуальными, соответственно используется динамическое (или, как его еще называют, позднее) связывание. Поэтому на этапе выполнения JVM определяет тип объекта B, на который ссылается указатель myA и вызывает соответствующую реализацию метода doA() класса B.

Ответ 2



Если вкратце, то всё дело в том, что у вас одинаковые имена переменных и вы фактически имеете две переменные с именем a и происходит Variable shadowing, т.е. когда одна переменная перекрывает другую. В вашем же случае происходит еще и случай описанный в документации: If an expression name consists of a single Identifier, then there must be exactly one visible declaration denoting either a local variable, parameter or field in scope at the point at which the the Identifier occurs. Otherwise, a compile-time error occurs. If the declaration declares a final field, the meaning of the name is the value of that field. Otherwise, the meaning of the expression name is the variable declared by the declaration. Т.е. переменная ищется в скопе класса A, даже если это фактически является классом B. Если же вы хотите, чтобы менялось значение переменной для класса B. То надо сделать, например, так: class B extends A { //int a = 7; B() { a = 7; } // весь остальной код }

Ответ 3



Тип класса B, но в данном случае он скастован к классу A, а поскольку и в A и в B поле a приватное (по умолчанию, без модификатора, оно приватное), то мы обращаемся к переменной a именно класса A, где оно равно 5

Комментариев нет:

Отправить комментарий