C'è un modo per sovrascrivere le variabili di classe in 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();
}

La funzione doIt stamperà "papà". C'è un modo per farlo stampare "figlio"?


111
Se vedi mai un'elettricità statica protetta, esegui.
Tom Hawtin: affronta il

23
@ TomHawtin-tackline perdona la mia ignoranza, ma perché una statica protetta è disapprovata? Ho provato a cercare su Google ma non riesco a trovare una risposta chiara. Grazie
Tony Chan,

Risposte:


68

Sì. Ma per quanto riguarda la variabile, viene sovrascritta (Dare un nuovo valore alla variabile. Dare una nuova definizione alla funzione è Sostituisci).Just don't declare the variable but initialize (change) in the constructor or static block.

Il valore verrà riflesso quando si utilizza nei blocchi della classe genitore

se la variabile è statica, modificare il valore durante l'inizializzazione stessa con il blocco statico,

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

oppure cambia nel costruttore.

È inoltre possibile modificare il valore in un secondo momento in qualsiasi blocco. Si rifletterà in super classe


10
Penso che la risposta dovrebbe avere del codice per una migliore leggibilità. Basta cambiare l'implementazione di Son in class Son extends Dad { static { me = 'son' } }
Cléssio Mendes,

97

In breve, no, non c'è modo di sovrascrivere una variabile di classe.

Non sovrascrivi le variabili di classe in Java, le nascondi. La sostituzione è ad esempio dei metodi. Nascondersi è diverso da ignorare.

Nell'esempio che hai dato, dichiarando la variabile di classe con il nome 'me' nella classe Son, nascondi la variabile di classe che avrebbe ereditato dalla sua superclasse Dad con lo stesso nome 'me'. Nascondere una variabile in questo modo non influisce sul valore della variabile di classe 'me' nella superclasse Papà.

Per la seconda parte della tua domanda, su come farlo stampare "figlio", imposterò il valore tramite il costruttore. Sebbene il codice seguente si discosti abbastanza dalla tua domanda originale, lo scriverei in questo modo;

public class Person {
    private String name;

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

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

Il JLS fornisce molti più dettagli su come nascondersi nella sezione 8.3 - Dichiarazioni sul campo


1
La sostituzione è possibile avendo un blocco statico nella classe derivata
siddagrl

1
@siddagrl: puoi elaborare?
Mr_and_Mrs_D,

quale delle classi di OP Persondovrebbe sostituire in questo esempio?
n611x007,

1
@naxa Sone Daddovrebbero ereditare da Person, quindi chiamare i super("Son or Dad");loro costruttori.
Panzercrisis,

È considerato buono o cattivo nascondere variabili come quelle in Java?
Panzercrisis,

31

Sì, basta sostituire il printMe()metodo:

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

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

cosa succede se la libreria che si sta utilizzando non ha printMe()metodo o anche se ha quel metodo è un metodo statico?
Venkat Sudheer Reddy Aedama

1
Per rendere questo un esempio di vita più reale, potresti considerare che il metodo "printMe" potrebbe essere una funzione molto complicata con una logica che non vuoi ripetere. In questo esempio, vuoi solo cambiare il nome della persona, non la logica che la stampa. È necessario creare un metodo chiamato "getName" che si sovrascriverà per restituire "figlio" e il metodo printMe invocherebbe il metodo getName come parte della sua stampa.
Anello del

17

È possibile creare un getter e quindi sovrascrivere quel getter. È particolarmente utile se la variabile che stai sovrascrivendo è una sottoclasse di se stessa. Immagina che la tua superclasse abbia un Objectmembro, ma nella sottoclasse questo ora è più definito come un 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

Se hai intenzione di ignorarlo, non vedo un motivo valido per mantenere questo statico. Suggerirei l'uso dell'astrazione (vedi codice di esempio). :

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

Ora possiamo aggiungere il papà:

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

il figlio:

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

e papà ha incontrato una simpatica signora:

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

Sembra che abbiamo una famiglia, diciamo al mondo i loro nomi:

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 Output: Tommy Nancy Dad
System.err è lo stesso di cui sopra (ha solo un carattere rosso)
JOption Output:
Tommy quindi
Nancy poi
Dad


1
Il PO riguardava la modifica dell'accesso alla statica. Quindi il "me" era un "papà" o "figlio" generico - qualcosa che non ha bisogno di essere creato per ogni papà o figlio e quindi è statico.
RichieHH,

Sì, hai ragione, non ho visto che era statico. Haha, questo cambierebbe la mia risposta a circa 100 righe di codice in meno, se rispondessi affatto. Grazie per il testa a testa
nckbrz,

6

Sembra un difetto di progettazione.

Rimuovere la parola chiave statica e impostare la variabile, ad esempio nel costruttore. In questo modo Son imposta semplicemente la variabile su un valore diverso nel suo costruttore.


1
cosa c'è di sbagliato in questo? Se la variabile membro 'me' dovrebbe essere sostituibile nel tuo progetto, allora quella di Patrick è la soluzione corretta
Chii

In realtà, se il valore di me è lo stesso per ogni istanza, rimuovere semplicemente "statico" andrebbe bene. L'inizializzazione non deve essere nel costruttore.
Nate Parsons,

Giusto, anche se tecnicamente (in bytecode) penso che sia quasi lo stesso ;-)
Patrick Cornelissen,

4

Anche se è vero che le variabili di classe possono essere nascoste solo nelle sottoclassi e non sovrascritte, è ancora possibile fare ciò che vuoi senza sovrascrivere printMe ()nelle sottoclassi e la riflessione è tua amica. Nel codice seguente ometto la gestione delle eccezioni per chiarezza. Si noti che dichiarare mecome protectednon sembra avere molto senso in questo contesto, poiché verrà nascosto in sottoclassi ...

class Dad
  {
    static String me = "dad";

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

Molto interessante, Java.lang.reflect eh? ... +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();
}

... stamperà "figlio".


non è lo stesso della domanda originale?
Corley Brigman,

Sai spiegare perché? (nella tua risposta)
Mr_and_Mrs_D

3

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

Si chiama Hiding Fields

Dal link sopra

All'interno di una classe, un campo che ha lo stesso nome di un campo nella superclasse nasconde il campo della superclasse, anche se i loro tipi sono diversi. All'interno della sottoclasse, non è possibile fare riferimento al campo nella superclasse con il suo nome semplice. Invece, è necessario accedere al campo tramite super, che sarà trattato nella sezione successiva. In generale, non è consigliabile nascondere i campi in quanto rende difficile la lettura del codice.


1
Un collegamento a una potenziale soluzione è sempre il benvenuto, ma ti preghiamo di aggiungere un contesto al collegamento in modo che i tuoi colleghi utenti abbiano idea di cosa sia e perché sia ​​lì. Cita sempre la parte più pertinente di un link importante, nel caso in cui il sito di destinazione non sia raggiungibile o rimanga permanentemente offline. Tieni presente che essere a malapena più di un collegamento a un sito esterno è una possibile ragione del perché e come vengono eliminate alcune risposte? .
FelixSFD

2

solo sostituendo printMe():

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

il riferimento a menel Dad.printMemetodo punta implicitamente al campo statico Dad.me, quindi in un modo o nell'altro stai cambiando cosa printMefa in Son...


2

Non è possibile ignorare le variabili in una classe. Puoi ignorare solo i metodi. Dovresti mantenere le variabili private altrimenti potresti avere molti problemi.


Non è possibile ignorare alcuna statica.
Tom Hawtin: affronta il

(o almeno non ha senso, IFSWIM.)
Tom Hawtin - affronta il

Corretto, non è possibile ignorare la statica. Puoi SHADOW o alcune persone mi hanno chiamato MASKING.
Dale,

2

In effetti stampa "papà", poiché il campo non è ignorato ma nascosto. Esistono tre approcci per farlo stampare "figlio":

Approccio 1: sovrascrivere 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();
}

Approccio 2: non nascondere il campo e inizializzarlo nel costruttore

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

Approccio 3: utilizzare il valore statico per inizializzare un campo nel costruttore

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

Le variabili non prendono parte al superamento. Solo i metodi lo fanno. Una chiamata di metodo viene risolta in fase di esecuzione, ovvero la decisione di chiamare un metodo viene presa in fase di esecuzione, ma le variabili vengono decise solo in fase di compilazione. Quindi quella variabile viene chiamata il cui riferimento viene usato per chiamare e non per l'oggetto runtime.

Dai un'occhiata al seguente frammento:

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

}

Quando esegui il codice, la console mostrerà:

Inside Honda
Inside Honda
Inside bike

Max_Speed = 150
Max_Speed = 90
Max_Speed = 90

0

Ovviamente usare attributi privati, e getter e setter sarebbero la cosa consigliata da fare, ma ho testato quanto segue e funziona ... Vedi il commento nel codice

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

Quindi ... ho appena ridefinito le regole dell'eredità o ho messo Oracle in una situazione difficile? Per me, String String protetto è chiaramente ignorato, come puoi vedere quando esegui questo programma. Inoltre, non ha alcun senso per me il motivo per cui gli attributi non dovrebbero essere sostituibili.


1
In questo caso stai "nascondendo" la variabile dalla super classe. Questo è distinto dal prevalere e non ha nulla a che fare con l'eredità. Le altre classi vedranno una variabile o l'altra a seconda che facciano riferimento alla classe base o alla sottoclasse e quella che vedono viene risolta al momento della compilazione. Se cambiassi il nome "me" nella sottoclasse in qualcos'altro otterresti lo stesso effetto. La maggior parte degli IDE e degli strumenti di convalida del codice (findbugs ecc.) Ti avvisano quando nascondi variabili come questa, dal momento che di solito non è quello che vuoi fare.
AutomatedMike,

Stai guardando la programmazione solo dal punto di vista della programmazione. Va bene, codice come vuoi in quel caso. Se osservi la codifica dal punto di vista di un membro del team, le regole diventano chiare, ad esempio perché i campi non sono sostituibili e così via. Potrei farti un discorso sul perché, ma se non lo vedi da quella prospettiva di un membro del team, vedrai i miei punti come non validi e mi respingerai argomenti non validi.
nckbrz,

0

Perché dovresti sovrascrivere le variabili quando puoi riassegnarle facilmente nelle sottoclassi.

Seguo questo schema per aggirare il design del linguaggio. Supponi un caso in cui hai una pesante classe di servizio nel tuo framework che deve essere utilizzata in diversi gusti in più applicazioni derivate. In tal caso, il modo migliore per configurare la logica della super classe è riassegnando le sue variabili di "definizione".

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

No. Le variabili di classe (applicabili anche alle variabili di istanza) non presentano funzionalità di override in Java poiché le variabili di classe vengono invocate sulla base del tipo di oggetto chiamante. Aggiunta un'altra classe (Human) nella gerarchia per renderlo più chiaro. Quindi ora abbiamo

Il figlio si estende papà si estende umano

Nel codice seguente, proviamo a scorrere su una matrice di oggetti Human, Dad and Son, ma stampa i valori di Human Class in tutti i casi poiché il tipo di oggetto chiamante era 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
        }
    }
}

Stamperà

  • umano
  • umano
  • umano

Quindi nessuna sostituzione delle variabili di classe.

Se vogliamo accedere alla variabile di classe dell'oggetto reale da una variabile di riferimento della sua classe genitrice, dobbiamo dirlo esplicitamente al compilatore lanciando il riferimento genitore (Oggetto umano) nel suo tipo.

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

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

Stamperà

  • papà
  • figlio

Su come parte di questa domanda: - Come già suggerito, sovrascrivere il metodo printMe () nella classe Son, quindi su chiamata

Son().printMe();

La variabile di classe "me" di Dad sarà nascosta perché la dichiarazione più vicina (dal metodo printme () della classe Son) del "me" (nella classe Son) avrà la precedenza.


0

Basta chiamare super.variable nel costruttore della sottoclasse

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

}

}

L'output è 10.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.