Объясните, как работает скрытие переменных в этом коде Java - PullRequest
0 голосов
/ 29 июня 2018

Рассмотрим ниже код

class A
{
    int x = 5;
    void foo()
    {
        System.out.println(this.x);
    }
}
class B extends A
{
    int x = 6;
    // some extra stuff
}
class C
{
    public static void main(String args[])
    {
         B b = new B();
         System.out.println(b.x);
         System.out.println(((A)b).x);
         b.foo();
    }
 }  

Вывод программы

6
5
5

Я понимаю первые два, но не могу разобраться с последним. Как b.foo () печатает 5. Класс B унаследует метод foo. Но не должен ли он печатать то, что печатал бы b.x? Что именно здесь происходит?

Ответы [ 5 ]

0 голосов
/ 30 июня 2018

при звонке

b.foo(); 

Он проверяет, переопределил ли B метод foo(), которого у него нет. Затем он смотрит на один уровень вверх до суперкласса A и вызывает этот метод.

Затем вы вызвали A версию foo(), которая затем печатает

this.x

Теперь A не может видеть B версию x.


Чтобы решить эту проблему, вы должны переопределить метод в B

class B extends A
{
    int x = 6;

    @Override
    void foo()
    {
        System.out.println(this.x);
    }

}

Теперь звоните

b.foo();

вызовет B версию foo(), и вы получите ожидаемый результат.

0 голосов
/ 29 июня 2018

В JAVA методы могут быть переопределены, а переменные - нет. Таким образом, поскольку ваш метод foo не переопределяется в B, он принимает переменную-член из A.

0 голосов
/ 29 июня 2018

Поля не могут быть переопределены в Java и подклассах с теми же именами полей, что и родительский класс shadow, «только» поля родительского класса.
Так this.x относится к x, определенному в текущем классе: A.
Тогда как результат: 5.

Если быть более точным: метод foo() наследуется подклассом B, но это не означает, что поведение наследуемого метода изменится в отношении полей экземпляра, на которые имеются ссылки, поскольку указанные поля не могут быть переопределены: 1011 * выражение, которое ссылается на поле A.x в методе foo(), продолжает ссылаться на A.x.

Это то же самое, что и для двух предыдущих утверждений:

 B b = new B();
 System.out.println(b.x); // refers B.x -> 6
 System.out.println(((A)b).x); // refers A.x -> 5
 b.foo(); // refers under the hood A.x -> 5

Очень хороший ответ rgettman показывает, как вы можете преодолеть скрытие поля в подклассе.
Альтернатива преодоления сокрытия заключается в создании поля экземпляра private (что рекомендуется) и предоставлении метода, который возвращает значение.
Таким образом, вы получаете выгоду от механизма переопределения, и скрытие полей больше не является проблемой для клиентов классов:

class A
{
    private int x = 5;

    int getX(){
        return x;
    }

    void foo()
    {
        System.out.println(this.getX());
    }
}
class B extends A
{
    private int x = 6;

    int getX(){
        return x;
    }
}
0 голосов
/ 29 июня 2018

Ну, это из-за статической привязки.

1) Статическое связывание в Java происходит во время компиляции, пока динамический привязка происходит во время выполнения.

2) частные методы, финальные методы и статические методы и переменные использует статическое связывание и связывается компилятором, в то время как виртуальные методы связан во время выполнения на основе объекта времени выполнения.

3) Статическая привязка использует информацию Типа (класса в Java) для привязки в то время как динамическое связывание использует объект для разрешения связывания.

4) Перегруженные методы связываются с использованием статического связывания при переопределении методы связаны с использованием динамического связывания во время выполнения.

0 голосов
/ 29 июня 2018

Да, класс B наследует метод foo. Но переменная x в B скрывает x в A; это не заменит его.

Это вопрос области видимости. Метод foo в A видит только те переменные, которые находятся в области видимости. Единственная переменная в области видимости - это переменная экземпляра x в A.

Метод foo наследуется, но не переопределяется, в B. Если вам нужно явно переопределить foo с тем же точным кодом:

class B extends A
{
    int x = 6;

    @Override
    void foo()
    {
        System.out.println(this.x);
    }
}

Тогда переменная, которая будет находиться в области видимости, когда на нее ссылается this.x, будет иметь значение B x, и будет напечатано 6. Хотя текст метода один и тот же, ссылка отличается из-за области действия.

Кстати, если вы действительно хотите сослаться на A s x в классе B, вы можете использовать super.x.

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