Есть ли способ переопределить переменные класса в Java? - PullRequest
142 голосов
/ 26 марта 2009
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 выведет «папа». Есть ли способ заставить его печатать "сын"?

Ответы [ 16 ]

93 голосов
/ 06 ноября 2009

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

Вы не переопределяете переменные класса в 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 - Объявление полей

56 голосов
/ 01 февраля 2015

Да. Но что касается переменной, то она перезаписывается (Давать новое значение переменной. Давать новое определение функции - Переопределить).

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

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

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

или другое изменение в конструкторе.

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

31 голосов
/ 26 марта 2009

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

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

        @Override
        public void printMe() {
                System.out.println(me);
        }
}
16 голосов
/ 08 июля 2011

Вы можете создать геттер и затем переопределить этот геттер. Это особенно полезно, если переменная, которую вы переопределяете, является подклассом сама по себе. Представьте, что в вашем суперклассе есть член 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"
}
10 голосов
/ 14 апреля 2014

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

     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: папа Томми Нэнси
System.err такой же, как и выше (только красный шрифт)
Вывод JOption:
Томми , затем
Нэнси , затем
Папа

6 голосов
/ 26 марта 2009

Это похоже на недостаток дизайна.

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

4 голосов
/ 15 июля 2013

Хотя верно, что переменные класса могут быть скрыты только в подклассах и не могут быть переопределены, все же можно делать то, что вы хотите, не переопределяя 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));
      }
  }
3 голосов
/ 18 октября 2013
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();
}

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

2 голосов
/ 06 января 2017

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

Это называется Скрытые поля

По ссылке выше

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

2 голосов
/ 17 апреля 2015

Это действительно печатает «папа», так как поле не переопределено, но скрыто. Есть три подхода, чтобы заставить его печатать «сын»:

Подход 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();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...