Подтвердить что ты не робот

Как наследование полей экземпляров работает в этом конкретном коде?

class A
{
    int a = 2, b = 3;
    public void display()
    {
        int c = a + b;
        System.out.println(c);
    }
}
class B extends A
{
    int a = 5, b = 6;
}
class Tester
{
    public static void main(String arr[])
    {
        A x = new A();
        B y = new B();
        x.display();
        y.display();
    }
}

Почему выход выходит как 5,5? И не 5,11?. Как работает метод y.display()?

4b9b3361

Ответ 1

почему вывод получается 5,5?

Потому что A.display() знает только о полях A.a и A.b. Это единственные поля, о которых знает любой код в A. Похоже, вы ожидаете, что объявления в B будут "переопределять" существующие объявления полей. Они этого не делают. Они объявляют новые поля, которые скрывают существующие поля. Переменные не ведут себя практически так, как это делают методы - концепция переопределения переменной просто не существует. Из JLS раздел 8.3:

Если класс объявляет поле с определенным именем, то объявление этого поля, как говорят, скрывает любые и все доступные объявления полей с тем же именем в суперклассах и суперинтерфейсы класса.

Вы можете получить необходимый эффект, изменив B так, чтобы его конструктор изменил значения существующих полей, которые он наследует от A:

class B extends A {
    B() {
        a = 5;
        b = 6;
    }
}

Обратите внимание, что это не объявления переменных. Это просто задания. Конечно, в большинстве кодов (ну, в большинстве случаев, я все равно видел) поля в A были бы частными, поэтому доступ к ним из B невозможен, но это просто пример с целью объяснения поведения языка.

Ответ 2

В классе A вы объявляете поля A и b. Метод display использует эти поля. В классе b вы объявляете НОВЫЕ поля с тем же именем. Вы фактически скрываете старые поля, не "переопределяя" их. Чтобы назначить разные значения для одних и тех же полей, используйте конструктор:

class A {
    A(int a, int b) {
        this.a = a;
        this.b = b;
    }

    A() {
        this(2, 3);
    }

    int a,b;

    public void display() {
        int c=a+b;
        System.out.println(c);
    }
}

class B extends A {
    B() {
        super(5, 6);
    }
}

Ответ 3

При этом:

class B extends A
{
    int a = 5, b = 6;
}

вы не переопределяете a и b, вы создаете новые переменные с одинаковыми именами. Таким образом, вы получаете четыре переменные (A.a, A.b, B.a, B.b).

Когда вы вызываете display() и вычисляете значение c, будут использоваться A.a и A.b, а не B.a и B.b

Ответ 4

Существует нечто, называемое переменным переопределением. Вот почему вы получаете одинаковый результат в обоих случаях.

Ответ 5

Причина в том, что Java использует концепцию лексической области для разрешения переменных.

В сущности, существует две возможности решения свободных переменных в функции ( "свободный" означает не локальный и не привязанный к параметрам функции):

1) против среды, в которой объявлена ​​функция

2) против среды, в которой функция выполняется (называется)

Java идет первым путем, поэтому свободные переменные в методах разрешаются [статически, во время компиляции] в отношении их лексической области (среды), которая включает в себя:

  • параметры метода и локальные переменные метода
  • декларации полей в классе, содержащем декларацию метода
  • объявления открытого поля в родительском классе
  • и т.д., вверх по цепочке наследования

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

Это противоположно тому, как методы работают в Java:

class A {
    public void foo() {
        boo();
    }
    public void boo() {
        System.out.println("A");
    }
}
class B extends A {
    @Override
    public void boo() {
        System.out.println("B");
    }
}
class Main {
    public static void main(String[] args) {
        B b = new B();
        b.foo(); // outputs "B"
    }
}

Это называется динамической диспетчером: вызов метода динамически решается во время выполнения против фактического объекта, на который он вызывается.

Ответ 6

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

class A extends java.lang.Object
{
    int a=2,b=3;
    public void display()
    {
        int c=a+b;
        System.out.println(c);
    }
}
class B extends A
{
    int a = 5, b = 6;
    public void display()
    {
      super(); //When you call y.display() then this statement executes.
    }
}
class Tester
{
    public static void main(String arr[])
    {
        A x = new A();
        B y = new B();
        x.display();
        y.display();
    }
}

И, следовательно, при супервызове вызывается метод class A.

Теперь перейдите к методу class A. Здесь int c = a + b; означает c = this.a + this.b;, который равен 2 + 3.

И результат 5.

Ответ 7

Класс B объявляет переменные в области B, public void display() является частью класса A и знает только свои собственные переменные области видимости.

Ответ 8

Это функционал наследования, который дает выход 5,5.

Ответ 9

Java не имеет ничего похожего на переопределение переменной. Таким образом, при вызове метода display() он обращается к переменным внутри родительского класса "A", а не к переменным внутри подкласса "B".

Это можно объяснить по той же причине, почему вы не можете напечатать переменную, объявленную в подклассе (а не в суперклассе) внутри метода суперкласса. Метод суперкласса просто не имеет доступа к переменным подкласса.

Однако вы сможете распечатать 5,11, если у вас есть методы доступа к полям обоих классов, и вы используете эти методы доступа, чтобы получить значения вместо прямого доступа с использованием имен переменных. (даже если метод display() присутствует только в суперклассе). Это связано с тем, что вызывается переопределенные методы доступа (во втором случае), которые возвращают значения из подкласса.