Есть ли способ получить доступ к значениям переменных конкретного класса из конкретных методов абстрактного класса

У меня есть понимание скрытие переменных и переопределение методов и вызов виртуального метода в Java. Мой вопрос: почему скрытие переменных не действует в унаследованных методах? Означает ли это, что мы должны переопределить методы, которые обращаются к этим переменным в каждом дочернем классе?

Абстрактный класс

public abstract class ClassA{

    protected int i = 0;

    public void printOurStuff(){
        System.out.println(i);
    }

    public void printMyStuff(){
        System.out.println(this.i);
    }

    public void printSomeStuff(String s){
        System.out.println(s);
    }

}

Класс бетона

 public class ClassB extends ClassA{

     protected int i = 1;

     public static main(String[] args){
         ClassB b = new ClassB();
         b.printOurStuff();
         b.printMyStuff();
         b.printSomeStuff(b.i);
     }

 }

Результаты

0 0 1

РЕДАКТИРОВАТЬ - изменил модификатор доступа к полю с private на protected и добавил метод printOurStuff

Непонятно, какой результат вы хотите изменить - результат звонка printMyStuff или результат звонка printSomeStuff(b.i). Но это две совершенно разные переменные, и каждая переменная доступна для кода только в рамках одного и того же объявления класса, поскольку они обе являются закрытыми переменными.

Jon Skeet 10.04.2019 08:33

Нет. Это означает, что вам следует избегать скрытия полей. Если i должен быть доступен в подклассах, сделайте его защищенным или добавьте защищенный метод получения, который вы можете вызывать в подклассах.

JB Nizet 10.04.2019 08:33

@JBNizet изменение модификатора доступа в любом случае не исправляет это. Однако я прошу объяснить, почему это происходит, а не как это сделать.

Kusal Hettiarachchi 10.04.2019 08:55

Это произойдет, если вы перестанете скрывать поля, то есть если вы удалите поле i в ClassB и, таким образом, получите одно поле i.

JB Nizet 10.04.2019 08:58

Обновлен вопрос, чтобы отразить интересующую меня проблему.

Kusal Hettiarachchi 10.04.2019 09:17
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
5
359
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Когда вы объявляете частные поля, такие как

  private int i = 0;

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

  protected int i = 0;

Чтобы переопределить значение этого поля, вы можете использовать статический блок, например:

public class ClassB extends ClassA {
    {
         i = 1;
    }
}

или присвойте новое значение в конструкторе:

public class ClassB extends ClassA {
     public ClassB() {
         i = 1;
     }
}

Что касается вашего примера, если вы проверите объект ClassB с помощью отладчика, вы обнаружите, что на самом деле у вас есть два поля i: одно для ClassA и одно для ClassB.

Обновлено:

Что касается случая, когда переменная i равна protected:
Посмотрите внимательно на определение классов.
Вы не можете не согласиться, что вы объявитьi поле два раза: для ClassA и ClassB. JVM будет уважать это заявление и следовать вашим инструкциям. Если поле protected или даже public, у вас все еще есть два поля. Вы не можете просто переопределить их, поскольку вы переопределяете методы. И при доступе к такому полю, как i = ..., вы фактически получаете доступ к ближайшему полю к вашей области. Для ClassB это его поле i, а не поле его суперкласса ClassA.

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

super.i = ...

super — ссылка на суперкласс.

Установка модификатора доступа на частный была ошибкой, которую я сделал при публикации вопроса. Хотя добавление статического блока и присвоение переменной в конструкторе вполне приемлемо в качестве решения этой проблемы, меня больше беспокоит то, почему это происходит. Это из-за того, что объем конкретных методов в абстрактном классе ограничен абстрактным классом?

Kusal Hettiarachchi 10.04.2019 08:59

@KusalHettiarachchi нет. Это потому, что поля не полиморфны. Их нельзя переопределить. Только методы могут быть переопределены. Итак, если переменная (здесь this) имеет тип ClassA, и вы обращаетесь к полю i этой переменной, то компилятор статически разрешает ее в ClassA.i.

JB Nizet 10.04.2019 09:02

@KusalHettiarachchi предоставил некоторые пояснения в ответе.

Pavel Smirnov 10.04.2019 09:07

@JBNizet У меня нет проблем с тем, что b.i разрешается до значения 1. Я хочу знать, почему метод printOurStuff оценивает значение i до значения, установленного в ClassA

Kusal Hettiarachchi 10.04.2019 09:16

@PavelSmirnovyour, мой вопрос заключается в методе printOutStuffоценке i как 0, хотя он вызывается для экземпляра ClassB.

Kusal Hettiarachchi 10.04.2019 09:18

И я ответил на это: потому что внутри printOurStuff неявная переменная this имеет тип ClassA, и, таким образом, i статически разрешается в поле i, объявленное в ClassA. Поля не полиморфны. Они разрешаются во время компиляции.

JB Nizet 10.04.2019 09:19

@KusalHettiarachchi, printOutStuff объявлен внутри ClassA. Он даже не знает о ClassB. Ссылка i в поле System.out.println(i); ссылки ClassA.i. Это то же самое, что и System.out.println(this.i);, где this — ссылка на ClassA.

Pavel Smirnov 10.04.2019 09:21

@KusalHettiarachchi, просто подумайте об этом так: вы не переопределяете поле i в ClassB, вы не переопределяете printOutStuff метод. Так почему же он должен использовать абсолютно новую переменную ClassB.i вместо ClassA.i?

Pavel Smirnov 10.04.2019 09:23

@PavelSmirnov, значит, скрытие переменных не работает для конкретных методов, реализованных в абстрактных методах, если только мы не переопределим методы в дочерних классах?

Kusal Hettiarachchi 10.04.2019 09:23

Тогда именно поэтому необходимо инициализировать переменную в конструкторе или в статическом блоке. Спасибо @PavelSmirnov за руководство

Kusal Hettiarachchi 10.04.2019 09:25

@KusalHettiarachchi, на самом деле вам не нужно инициализировать его в конструкторе или в статическом блоке, если у вас нет конфликта имен. Это просто проблема области: две переменные с одинаковыми именами, поэтому компилятор выбирает наиболее близкую к области видимости.

Pavel Smirnov 10.04.2019 09:29

Другие вопросы по теме