Есть ли способ переопределить переменные класса в Java?

class Dad
{
    protected static String me = "dad";

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    protected static String me = "son";
}

public void doIt()
{
    new Son().printMe();
}

Функция doIt будет печатать "папа". Есть ли способ заставить его напечатать "сын"?

Ответ 1

Да. Но в качестве переменной она перезаписывается (предоставление нового значения переменной. Придание новому определению функции Override). Just don't declare the variable but initialize (change) in the constructor or static block.

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

если переменная является статической, то сначала измените значение при инициализации статическим блоком,

class Son extends Dad {
    static { 
       me = 'son'; 
    }
}

или изменить конструктор.

Вы также можете изменить значение позже в любых блоках. Он будет отражен в суперклассе

Ответ 2

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

Вы не переопределяете переменные класса в Java, которые вы скрываете. Переопределение - это, например, методы. Скрытие отличается от переопределения.

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

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

public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public void printName() {
        System.out.println(name);
    }
}

JLS дает намного больше информации о скрытии в разделе 8.3 - Объявления полей

Ответ 3

Да, просто переопределите метод printMe():

class Son extends Dad {
        public static final String me = "son";

        @Override
        public void printMe() {
                System.out.println(me);
        }
}

Ответ 4

Вы можете создать getter и затем переопределить этот getter. Это особенно полезно, если переменная, которую вы переопределяете, является подклассом самого себя. Представьте, что ваш суперкласс имеет член Object, но в вашем подклассе это теперь более определено как Integer.

class Dad
{
        private static final String me = "dad";

        protected String getMe() {
            return me;
        }

        public void printMe()
        {
                System.out.println(getMe());
        }
}

class Son extends Dad
{
        private static final String me = "son";

        @Override
        protected String getMe() {
            return me;
        }
}

public void doIt()
{
        new Son().printMe(); //Prints "son"
}

Ответ 5

Если вы собираетесь переопределить его, я не вижу веской причины сохранять этот статический. Я бы предложил использовать абстракцию (см. Пример кода).:

     public interface Person {
        public abstract String getName();
       //this will be different for each person, so no need to make it concrete
        public abstract void setName(String name);
    }

Теперь мы можем добавить папу:

public class Dad implements Person {

    private String name;

    public Dad(String name) {
        setName(name);
    }

    @Override
    public final String getName() {
    return name;
    }

    @Override
    public final void setName(String name) {
        this.name = name;
    }
}

сын:

public class Son implements Person {

    private String name;

    public Son(String name) {
        setName(name);
    }

    @Override
    public final String getName() {
        return name;
    }

    @Override
    public final void setName(String name) {
        this.name = name;
    }
}

и папа встретил милую даму:

public class StepMom implements Person {

    private String name;

    public StepMom(String name) {
        setName(name);
    }

    @Override
    public final String getName() {
        return name;
    }

    @Override
    public final void setName(String name) {
        this.name = name;
    }
}

Похоже, у нас есть семья, давайте расскажите миру свои имена:

public class ConsoleGUI {

    public static void main(String[] args) {
        List<Person> family = new ArrayList<Person>();
        family.add(new Son("Tommy"));
        family.add(new StepMom("Nancy"));
        family.add(new Dad("Dad"));
        for (Person person : family) {
            //using the getName vs printName lets the caller, in this case the
            //ConsoleGUI determine versus being forced to output through the console. 
            System.out.print(person.getName() + " ");
            System.err.print(person.getName() + " ");
            JOptionPane.showMessageDialog(null, person.getName());
    }
}

}

Выход System.out: Tommy Nancy Dad
System.err - это то же самое, что и выше (только красный шрифт)
Выход JOption:
Томми,

Нэнси,

Папа

Ответ 6

Это выглядит как дефект дизайна.

Удалите статическое ключевое слово и задайте переменную, например, в конструкторе. Таким образом, Сын просто устанавливает переменную в другое значение в своем конструкторе.

Ответ 7

Хотя верно, что переменные класса могут быть скрыты только в подклассах, а не переопределены, все же можно делать то, что вы хотите, не переопределяя printMe () в подклассах, а размышление - ваш друг. В приведенном ниже коде я опускаю обработку исключений для ясности. Обратите внимание, что объявление me как protected, похоже, не имеет большого смысла в этом контексте, поскольку оно будет скрыто в подклассах...

class Dad
  {
    static String me = "dad";

    public void printMe ()
      {
        java.lang.reflect.Field field = this.getClass ().getDeclaredField ("me");
        System.out.println (field.get (null));
      }
  }

Ответ 8

class Dad
{
    protected static String me = "dad";

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    protected static String _me = me = "son";
}

public void doIt()
{
    new Son().printMe();
}

... напечатает "сын".

Ответ 9

только путем переопределения printMe():

class Son extends Dad 
{
    public void printMe() 
    {
        System.out.println("son");
    }
}

ссылка на me в методе Dad.printMe неявно указывает на статическое поле Dad.me, поэтому так или иначе вы меняете то, что printMe делает в Son...

Ответ 10

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

Ответ 11

Он действительно печатает "папа", поскольку поле не переопределено, а скрыто. Существует три подхода, чтобы заставить его печатать "сын":

Подход 1: переопределение printMe

class Dad
{
    protected static String me = "dad";

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    protected static String me = "son";

    @override
    public void printMe()
    {
        System.out.println(me);
    }
}

public void doIt()
{
    new Son().printMe();
}

Подход 2: не спрячьте поле и не инициализируйте его в конструкторе

class Dad
{
    protected static String me = "dad";

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    public Son()
    {
        me = "son";
    }
}

public void doIt()
{
    new Son().printMe();
}

Подход 3: используйте статическое значение для инициализации поля в конструкторе

class Dad
{
    private static String meInit = "Dad";

    protected String me;

    public Dad() 
    {
       me = meInit;
    }

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    private static String meInit = "son";

    public Son()
    {
        me = meInit;
    }

}

public void doIt()
{
    new Son().printMe();
}

Ответ 12

https://docs.oracle.com/javase/tutorial/java/IandI/hidevariables.html

Он называется "Скрытие полей"

Из ссылки выше

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

Ответ 13

Переменные не участвуют в отбрасывании. Только методы делают. Вызов метода разрешается во время выполнения, то есть решение о вызове метода принимается во время выполнения, но переменные определяются только во время компиляции. Следовательно, вызывается эта переменная, чья ссылка используется для вызова, а не для объекта времени выполнения.

Посмотрите на следующий фрагмент:

package com.demo;

class Bike {
  int max_speed = 90;
  public void disp_speed() {
    System.out.println("Inside bike");
 }
}

public class Honda_bikes extends Bike {
  int max_speed = 150;
  public void disp_speed() {
    System.out.println("Inside Honda");
}

public static void main(String[] args) {
    Honda_bikes obj1 = new Honda_bikes();
    Bike obj2 = new Honda_bikes();
    Bike obj3 = new Bike();

    obj1.disp_speed();
    obj2.disp_speed();
    obj3.disp_speed();

    System.out.println("Max_Speed = " + obj1.max_speed);
    System.out.println("Max_Speed = " + obj2.max_speed);
    System.out.println("Max_Speed = " + obj3.max_speed);
  }

}

Когда вы запустите код, консоль отобразит:

Inside Honda
Inside Honda
Inside bike

Max_Speed = 150
Max_Speed = 90
Max_Speed = 90

Ответ 14

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

class Dad
{
    protected static String me = "dad";

    public void printMe()
    {
        System.out.println(me);
    }
}

class Son extends Dad
{
    protected static String me = "son";

    /* 
    Adding Method printMe() to this class, outputs son 
    even though Attribute me from class Dad can apparently not be overridden
    */

    public void printMe()
    {
        System.out.println(me);
    }
}

class Tester
{
    public static void main(String[] arg)
    {
        new Son().printMe();
    }
}

Sooo... Я просто переопределил правила наследования или я поставил Oracle в сложную ситуацию? Для меня защищенная статическая строка меня явно переопределена, как вы можете видеть, когда выполняете эту программу. Кроме того, мне не имеет никакого смысла, почему атрибуты не должны быть переопределяемыми.

Ответ 15

Почему вы хотите переопределить переменные, когда вы можете легко переназначить их в подклассах.

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

public interface ExtensibleService{
void init();
}

public class WeightyLogicService implements ExtensibleService{
    private String directoryPath="c:\hello";

    public void doLogic(){
         //never forget to call init() before invocation or build safeguards
         init();
       //some logic goes here
   }

   public void init(){}    

}

public class WeightyLogicService_myAdaptation extends WeightyLogicService {
   @Override
   public void init(){
    directoryPath="c:\my_hello";
   }

}

Ответ 16

Нет. Переменные класса (также применимые к переменным экземпляра) не отображают функцию переопределения в Java, поскольку переменные класса вызывается по типу вызывающего объекта. Добавлен еще один класс (Human) в иерархии, чтобы сделать его более понятным. Итак, теперь у нас есть

Сын расширяет Папа расширяет Человеческий

В приведенном ниже коде мы пытаемся выполнить итерацию по массиву объектов Human, Dad and Son, но во всех случаях он печатает значения класса Human, поскольку тип вызывающего объекта - это Human.

    class Human
{
    static String me = "human";

    public void printMe()
    {
        System.out.println(me);
    }
}
class Dad extends Human
{
    static String me = "dad";

}

class Son extends Dad
{
    static String me = "son";
}


public class ClassVariables {
    public static void main(String[] abc)   {
        Human[] humans = new Human[3];
        humans[0] = new Human();
        humans[1] = new Dad();
        humans[2] = new Son();
        for(Human human: humans)   {
            System.out.println(human.me);        // prints human for all objects
        }
    }
}

Будет распечатан

  • человек
  • человек
  • человек

Таким образом, переопределение переменных класса не происходит.

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

    System.out.println(((Dad)humans[1]).me);        // prints dad

    System.out.println(((Son)humans[2]).me);        // prints son

Будет распечатан

  • папа
  • сын

О том, как часть этого вопроса: - Как уже было предложено переопределить метод printMe() в классе Son, тогда при вызове

Son().printMe();

Переменная класса "я" класса "папа" будет скрыта, потому что ближайшее объявление (из метода класса son printme() от "me" (в классе Son) получит преимущество.

Ответ 17

Просто вызовите super.variable в конструкторе подкласса

public abstract class Beverage {

int cost;


int getCost() {

    return cost;

}

}'

public class Coffee extends Beverage {


int cost = 10;
Coffee(){
    super.cost = cost;
}


}'

public class Driver {

public static void main(String[] args) {

    Beverage coffee = new Coffee();

    System.out.println(coffee.getCost());

}

}

Вывод 10.