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

Скрытие переменных экземпляра класса

Мне интересно, почему Java имеет такое странное поведение в отношении суперкласса и подкласса с переменными экземпляра с тем же именем.

Скажем, мы имеем следующие определения классов:

class Parent {
    int var = 1;
}

class Child extends Parent {
    int var = 2;
}

Таким образом, мы должны спрятать переменную суперкласса var. И если мы явно не укажем способ доступа к Parent var с помощью вызова super, мы никогда не сможем получить доступ к var из экземпляра дочернего элемента.

Но когда у нас есть бросок, этот механизм скрытия ломается:

Child child = new Child();
Parent parent = (Parent)child;
System.out.println(parent.var); // prints out 1, instead of 2

Разве это не полностью обходит всю точку сокрытия? Если это так, то разве это не делает идею совершенно бесполезной?

РЕДАКТИРОВАТЬ. Я специально ссылаюсь на эту статью в учебниках по Java. Он упоминает

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

Из того, что я там читаю, кажется, подразумевается, что разработчики Java имели в виду какую-то технику. Хотя я согласен, что это довольно неясная концепция и, вероятно, будет плохой практикой в ​​целом.

4b9b3361

Ответ 1

В Java члены данных не являются полиморфными. Это означает, что Parent.var и Child.var являются двумя разными переменными, которые имеют одно и то же имя. Вы ни в каком смысле не "переопределяете" var в производном классе; как вы обнаружили, обе переменные могут быть доступны независимо друг от друга.

Лучший способ продвижения действительно зависит от того, чего вы пытаетесь достичь:

  • Если Parent.var не должно быть видимым для Child, сделайте его private.
  • Если Parent.var и Child.var являются двумя логически отличными переменными, дайте им разные имена, чтобы избежать путаницы.
  • Если Parent.var и Child.var являются логически одной и той же переменной, тогда используйте для них один элемент данных.

Ответ 2

"Точка" скрытия поля - это просто указать поведение кода, который дает переменную с тем же именем, что и в ее суперклассе.

Он не предназначен для использования в качестве метода, чтобы искренне скрывать информацию. Это делается с учетом того, что частные переменные начинаются с... Я бы настоятельно рекомендовал использовать частные переменные практически во всех случаях. Поля - это деталь реализации, которая должна быть скрыта от всего другого кода.

Ответ 3

Атрибуты не являются полиморфными в Java, и в любом случае объявление публичного атрибута не всегда является хорошей идеей. Для поведения, которое вы ищете, лучше использовать частные атрибуты и методы доступа, например:

class Parent {

    private int var = 1;

    public int getVar() {
        return var;
    }

    public void setVar(int var) {
        this.var = var;
    }

}

class Child extends Parent {

    private int var = 2;

    public int getVar() {
        return var;
    }

    public void setVar(int var) {
        this.var = var;
    }

}

И теперь, при тестировании, мы получаем желаемый результат, 2:

Child child = new Child();
Parent parent = (Parent)child;
System.out.println(parent.getVar());

Ответ 4

Этот сценарий известен как спряжение переменных. Когда у дочернего и родительского классов есть переменная с тем же именем, переменная дочернего класса скрывает переменную родительского класса, и этот процесс называется спряжением переменной.

В Java переменные не являются полиморфными, а переменная Скрытие не такая же, как переопределение метода

Хотя спряжение переменной выглядит как переопределение переменной, аналогичной переопределению метода, но это не так, Overriding применим только к методам, а скрытие - применимые переменные.

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

Но в переменной hiding child класс скрывает унаследованные переменные вместо замены, поэтому, когда мы пытаемся получить доступ к переменной из родительской ссылки, удерживая дочерний объект, она будет доступна из родительского класса.

public static void main(String[] args) throws Exception {

    Parent parent = new Parent();
    parent.printInstanceVariable(); // Output - "Parent Instance Variable"
    System.out.println(parent.x); // Output - "Parent Instance Variable"

    Child child = new Child();
    child.printInstanceVariable();// Output - "Child Instance Variable, Parent Instance Variable"
    System.out.println(child.x);// Output - "Child Instance Variable"

    parent = child; // Or parent = new Child();
    parent.printInstanceVariable();// Output - "Child Instance Variable, Parent Instance Variable"
    System.out.println(parent.x);// Output - Parent Instance Variable

    // Accessing child variable from parent reference by type casting
    System.out.println(((Child) parent).x);// Output - "Child Instance Variable"
}

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

Объявление переменных с одним и тем же именем в дочерних и родительских целях создает путаницу, мы всегда должны избегать этого, чтобы не было путаницы. И поэтому мы также должны всегда придерживаться общих руководящих принципов создания POJO и объявлять наши переменные с помощью частного доступа, а также предоставлять надлежащие методы get/set для их доступа.

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

Ответ 5

Когда вы выполняете кастинг, вы фактически сообщаете компилятору "Я знаю лучше" - он приостанавливает обычные правила ввода текста и дает вам преимущество.

Говоря Parent parent = (Parent)child;, вы сообщаете компилятору "относиться к этому объекту так, как если бы это был экземпляр родителя".

С другой стороны, вы вводите в заблуждение принцип "скрытия информации" OO (хороший!) с побочным эффектом, скрывающим поле (обычно это плохо).

Ответ 6

Как вы указали:

мы должны спрятать переменную суперкласса var

Главное здесь - переменные не переопределять, как это делают методы, поэтому, когда вы вызываете непосредственно Child.var, вы вызываете переменную непосредственно из класса Child и когда вы вызываете Parent.var, вы вызываете переменную из класса Parent, независимо от того, имеют ли они одно и то же имя.

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