Чи є спосіб замінити змінні класу на Java?


161
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 друкує "тато". Чи є спосіб зробити так, щоб він надрукував "сина"?


111
Якщо ви коли-небудь бачите захищений статик, запустіть.
Том Хотін - тайклін

23
@ TomHawtin-tackline пробачте моє невігластво, але чому нахмурена статична нахмура? Я спробував гуглінг, але не можу знайти чітку відповідь. Спасибі
Тоні Чан

Відповіді:


68

Так. Але що стосується змінної, вона перезаписується (Надаючи нове значення змінній. Даючи нове визначення функції функція - Заміна).Just don't declare the variable but initialize (change) in the constructor or static block.

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

якщо змінна статична, то змініть значення під час самої ініціалізації статичним блоком,

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

інакше змінити конструктор.

Ви також можете змінити значення пізніше в будь-яких блоках. Це відобразиться в суперкласі


10
Я думаю, що у відповіді має бути якийсь код для кращої читабельності. Просто змініть реалізацію Son на class Son extends Dad { static { me = 'son' } }
Клесіо Мендес

97

Якщо коротко, ні, немає способу змінити змінну класу.

У Java не змінюються змінні класу, ви їх приховуєте. Переоцінка - це, наприклад, методи. Приховування відрізняється від перекриття.

У наведеному вами прикладі, оголошуючи змінну класу з іменем 'я' у класі Son, ви приховуєте змінну класу, яку вона отримала б у спадок від свого тата суперкласу з тим самим ім'ям 'я'. Приховування змінної таким чином не впливає на значення змінної класу 'я' у татові надкласу.

У другій частині вашого питання про те, як зробити так, щоб він надрукував "син", я встановив би значення через конструктор. Хоча код нижче від вашого оригінального запитання відхиляється досить багато, я б написав це щось подібне;

public class Person {
    private String name;

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

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

JLS надає набагато більше деталей щодо приховування в розділі 8.3 - Польові декларації


1
Переоцінка можлива, якщо статичний блок є у похідному класі
siddagrl

1
@siddagrl: чи можете ви детальніше?
Mr_and_Mrs_D

який з класів ОП Personповинен замінити в цьому прикладі?
n611x007

1
@naxa Sonі Dadповинні наслідувати від них Person, а потім викликати super("Son or Dad");їх конструктори.
Panzercrisis

Чи вважається добре чи погано приховувати такі змінні у Java?
Panzercrisis

31

Так, просто перекрийте printMe()метод:

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

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

що робити, якщо бібліотека, яку ви використовуєте, не має printMe()методу, або навіть у неї є метод - це статичний метод?
Venkat Sudheer Reddy Aedama

1
Щоб зробити це прикладом більш реального життя, ви можете врахувати, що метод "printMe" може бути дуже складною функцією з логікою, яку ви не хочете повторювати. У цьому прикладі ви просто хочете змінити ім’я людини, а не логіку, яка її друкує. Ви повинні створити метод під назвою "getName", який ви б замінили, щоб повернути "син", а метод printMe буде викликати метод getName як частину його друку.
Кільце

17

Ви можете створити геттер, а потім змінити його. Це особливо корисно, якщо змінна, яку ви переосмислюєте, сама по собі є підкласом. Уявіть, що у вашого суперкласу є Objectучасник, але у вашому підкласі це тепер більше визначено як "an" 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"
}

11

Якщо ви збираєтесь її переосмислити, я не бачу вагомих причин зберігати цю статику. Я б запропонував використовувати абстракцію (див. Приклад коду). :

     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.err такий же, як вище (просто має червоний шрифт)
Вихід з роботи:
Томмі, потім
Ненсі, потім
тато


1
ОП стосувалася зміни доступу до статики. Отже, "я" був загальним "татом" або "сином" - те, що не потрібно створювати для кожного тата чи сина, а значить, є статичним.
RichieHH

Так, ваше право, я не бачив, що це статично. Ха-ха, це змінило б мою відповідь приблизно на 100 менших рядків коду, я, якби взагалі відповів. Дякую за голову вгору
nckbrz

6

Це схоже на ваду дизайну.

Видаліть статичне ключове слово та встановіть змінну, наприклад, у конструкторі. Таким чином Son просто встановлює змінну на інше значення у своєму конструкторі.


1
Що неправильно в цьому? Якщо змінна члена 'я' у вашому дизайні повинна бути надмірна, тоді патрік - це правильне рішення
Chii

Насправді, якщо значення для мене однакове для кожного екземпляра, просто видалити «статичне» було б добре. Ініціалізація не повинна бути в конструкторі.
Нейт Парсонс

Правильно, хоча технічно (у байт-коді) я думаю, що це майже те саме ;-)
Патрік Корнеліссен

4

Хоча правдиво, що змінні класу можуть бути приховані лише в підкласах, а не переосмислюватися, все ж можна робити те, що ви хочете, не змінюючи 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));
      }
  }

Дуже цікаво, Java.lang.reflect так? ... +1
nckbrz

3
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();
}

... надрукує "син".


Це не те саме, що оригінальне запитання?
Корлі Бригман

Чи можете ви пояснити, чому? (у вашій відповіді)
Mr_and_Mrs_D

3

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

Це називається приховування полів

За посиланням вище

У межах класу поле, яке має те саме ім’я, що і поле в надкласі, приховує поле суперкласу, навіть якщо їхні типи різні. У межах підкласу не можна посилатися на поле в надкласі простою назвою. Натомість до поля потрібно звертатися через супер, який висвітлюється в наступному розділі. Загалом кажучи, ми не рекомендуємо приховувати поля, оскільки це ускладнює читання коду.


1
Посилання на потенційне рішення завжди вітається, але будь ласка, додайте контекст навколо цього посилання, щоб ваші колеги користувачі мали уявлення про те, що це таке і чому воно існує. Завжди цитуйте найбільш релевантну частину важливого посилання на випадок, якщо цільовий сайт недоступний або перебуває постійно в режимі офлайн. Враховуйте, що наявність ледве більше ніж посилання на зовнішній сайт є можливою причиною того, чому і як видаляються відповіді? .
FelixSFD

2

тільки шляхом переосмислення printMe():

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

посилання meна Dad.printMeметод неявно вказує на статичному полі Dad.me, так що так чи інакше ви змінити те , що printMeвідбувається в Son...


2

Ви не можете змінювати змінні в класі. Ви можете перекрити лише методи. Ви повинні зберігати змінні приватними, інакше ви можете отримати багато проблем.


Ви не можете змінити будь-яку статику.
Том Хотін - тайклін

(або, принаймні, це не має сенсу, IFSWIM.)
Том Хоутін - таклін

Правильно, ви не можете перекрити статику. Ви можете їх заштрихувати або деякі люди називають мене МАСКІНГ.
Дейл

2

Це дійсно друкує "тато", оскільки поле не перекрито, а приховано. Існує три підходи, щоб зробити його друком "син":

Підхід 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();
}

2

Змінні не беруть участі в перезаписі. Тільки методи роблять. Виклик методу вирішується під час виконання, тобто рішення про виклик методу приймається під час виконання, але змінні визначаються лише під час компіляції. Отже, називається та змінна, посилання якої використовується для виклику, а не об'єкта виконання.

Погляньте на наступний фрагмент:

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

0

Звичайно, використання приватних атрибутів і getters та setters було б рекомендованою справою, але я перевірив наступне, і це працює ... Дивіться коментар у коді

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();
    }
}

Ооо ... я щойно переосмислив правила успадкування чи поставив Oracle у складну ситуацію? Для мене захищений статичний String me явно переосмислений, як ви бачите, коли виконуєте цю програму. Також для мене немає сенсу, чому атрибути не повинні бути перезаписовими.


1
У цьому випадку ви "ховаєте" змінну від суперкласу. Це відрізняється від переважаючого і не має нічого спільного з успадкуванням. Інші класи побачать одну чи іншу змінну залежно від того, посилаються вони на базовий клас або на підклас, а той, який вони бачать, фіксується під час компіляції. Якщо ви змінили ім'я "я" в підкласі на щось інше, ви отримали б такий самий ефект. Більшість інструментів ідентифікації IDE та інструментів перевірки коду (Findbugs тощо) попередить вас, коли ви приховуєте подібні змінні, оскільки зазвичай це не те, що ви хочете робити.
AutomatedMike

Ви дивитесь на програмування лише з точки зору кодування. Що нормально, але в цьому випадку введіть код. Якщо ви дивитесь на кодування з виду члена команди, то стають зрозумілі правила, наприклад, чому поля не можна замінити тощо. Я можу сказати вам про те, чому, але якщо ви не бачите це з точки зору члена команди, ви просто побачите мої бали як недійсні та відкинете неправдиві аргументи на мене.
nckbrz

0

Чому ви хочете змінити змінні, коли ви зможете легко перепризначити їх у підкласах.

Я дотримуюся цієї схеми, щоб вирішити мовний дизайн. Припустимо випадок, коли у вашому рамках є вагомий клас обслуговування, який потрібно використовувати в різних ароматах у кількох похідних програмах. У цьому випадку найкращим способом налаштування логіки суперкласу є перепризначення його змінних 'визначення'.

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";
   }

}

0

Ні. Змінні класу (також застосовні до змінних екземплярів) не демонструють переважаючої функції в Java, оскільки змінні класу викликаються на основі типу об'єкта виклику. Додано ще один клас (Людина) в ієрархії, щоб зробити це більш зрозумілим. Отже, тепер у нас є

Син розширює тато розширює Людського

У наведеному нижче коді ми намагаємось повторити масив об'єктів Людина, Тато і Син, але він друкує значення людського класу у всіх випадках, оскільки тип об'єкта, що викликає, був Людина.

    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
        }
    }
}

Буде надруковано

  • людські
  • людські
  • людські

Тому немає переосмислення змінних Class.

Якщо ми хочемо отримати доступ до змінної класу фактичного об'єкта з опорної змінної його батьківського класу, нам потрібно чітко сказати це компілятору, передавши посилання батьків (Human object) на його тип.

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

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

Буде надруковано

  • Папа
  • син

Про те, як частина цього питання: - Як уже запропоновано замінити метод printMe () у класі Son, то на дзвінок

Son().printMe();

Папа змінного класу "я" буде прихована, оскільки найближче оголошення (від методу Son print printme ()) "я" (в класі Son) отримає перевагу.


0

Просто викличте 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.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.