Existe-t-il un moyen de remplacer les variables de classe en 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 fonction doIt affichera "papa". Existe-t-il un moyen de le faire imprimer "fils"?

Dikla
la source
111
Si jamais vous voyez un statique protégé, exécutez.
Tom Hawtin - tackline
23
@ TomHawtin-tackline pardonne mon ignorance, mais pourquoi une statique protégée est-elle mal vue? J'ai essayé googler mais je ne trouve pas de réponse claire. Merci
Tony Chan

Réponses:

68

Oui. Mais comme la variable est concernée, elle est écrasée (Donner une nouvelle valeur à la variable. Donner une nouvelle définition à la fonction est Remplacer).Just don't declare the variable but initialize (change) in the constructor or static block.

La valeur sera reflétée lors de l'utilisation dans les blocs de la classe parent

si la variable est statique alors changez la valeur lors de l'initialisation elle-même avec un bloc statique,

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

ou bien changer de constructeur.

Vous pouvez également modifier la valeur ultérieurement dans n'importe quel bloc. Cela se reflétera dans la super classe

Vivek MVK
la source
10
Je pense que la réponse devrait avoir du code pour une meilleure lisibilité. class Son extends Dad { static { me = 'son' } }
Modifiez
97

En bref, non, il n'y a aucun moyen de remplacer une variable de classe.

Vous ne remplacez pas les variables de classe en Java, vous les masquez. Le remplacement concerne par exemple les méthodes. Le masquage est différent du dépassement.

Dans l'exemple que vous avez donné, en déclarant la variable de classe avec le nom 'me' dans la classe Son, vous masquez la variable de classe qu'elle aurait héritée de sa superclasse Dad avec le même nom 'me'. Cacher une variable de cette manière n'affecte pas la valeur de la variable de classe «me» dans la superclasse Dad.

Pour la deuxième partie de votre question, sur la façon de faire imprimer "fils", je définirais la valeur via le constructeur. Bien que le code ci-dessous s'écarte beaucoup de votre question initiale, je l'écrirais quelque chose comme ceci;

public class Person {
    private String name;

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

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

Le JLS donne beaucoup plus de détails sur le masquage dans la section 8.3 - Déclarations sur le terrain

Mike
la source
1
Le remplacement est possible en ayant un bloc statique dans la classe dérivée
siddagrl
1
@siddagrl: pouvez-vous élaborer?
Mr_and_Mrs_D
laquelle des classes d'OP est Personcensée remplacer dans cet exemple?
n611x007
1
@naxa Sonet Dadsont censés hériter de Person, puis appeler super("Son or Dad");leurs constructeurs.
Panzercrisis
Est-il considéré comme bon ou mauvais de cacher des variables comme ça en Java?
Panzercrisis
31

Oui, remplacez simplement la printMe()méthode:

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

        @Override
        public void printMe() {
                System.out.println(me);
        }
}
Romain Linsolas
la source
Et si la bibliothèque que vous utilisez n'a pas de printMe()méthode, ou même si elle a cette méthode, c'est une méthode statique?
Venkat Sudheer Reddy Aedama
1
Pour en faire un exemple plus réel, vous pouvez considérer que la méthode "printMe" peut être une fonction très compliquée avec une logique que vous ne voulez pas répéter. Dans cet exemple, vous voulez simplement changer le nom de la personne, pas la logique qui l'imprime. Vous devez créer une méthode appelée "getName" que vous remplaceriez pour retourner "son" et la méthode printMe invoquerait la méthode getName dans le cadre de son impression.
Anneau le
17

Vous pouvez créer un getter, puis remplacer ce getter. C'est particulièrement utile si la variable que vous remplacez est une sous-classe d'elle-même. Imaginez que votre super classe a un Objectmembre mais que dans votre sous-classe, il est désormais plus défini pour être 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"
}
John Strickler
la source
11

Si vous prévoyez de le remplacer, je ne vois pas de raison valable de garder ce statique. Je suggérerais l'utilisation de l'abstraction (voir l'exemple de code). :

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

Maintenant, nous pouvons ajouter le papa:

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

le fils:

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

et papa a rencontré une gentille dame:

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

On dirait que nous avons une famille, disons au monde leurs noms:

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 Sortie: Tommy Nancy Dad
System.err est le même que ci-dessus (a juste une police rouge)
Sortie JOption:
Tommy puis
Nancy puis
papa

nckbrz
la source
1
Le PO concernait la modification de l'accès à la statique. Par conséquent, le "moi" était un "papa" ou un "fils" générique - quelque chose qu'il n'est pas nécessaire de créer pour chaque père ou fils et qui est donc une statique.
RichieHH
Ouais, tu as raison, je n'ai pas vu que c'était statique. Haha, cela changerait ma réponse à environ 100 lignes de code de moins, si je répondais pas du tout. Merci pour la tête
nckbrz
6

Cela ressemble à un défaut de conception.

Supprimez le mot-clé static et définissez la variable par exemple dans le constructeur. De cette façon, Son définit simplement la variable sur une valeur différente dans son constructeur.

Patrick Cornelissen
la source
1
quoi de mal à ce sujet? Si la variable membre «moi» est censée être remplaçable dans votre conception, alors patrick est la bonne solution
Chii
En fait, si la valeur de moi est la même pour chaque instance, supprimer simplement «statique» serait bien. L'initialisation n'a pas besoin d'être dans le constructeur.
Nate Parsons
Bon, bien que techniquement (en bytecode) je pense que c'est presque la même chose ;-)
Patrick Cornelissen
4

Bien qu'il soit vrai que les variables de classe ne peuvent être masquées que dans les sous-classes et ne pas être remplacées, il est toujours possible de faire ce que vous voulez sans remplacer printMe () dans les sous-classes, et la réflexion est votre ami. Dans le code ci-dessous, j'omets la gestion des exceptions pour plus de clarté. Veuillez noter que déclarer meas protectedne semble pas avoir beaucoup de sens dans ce contexte, car il va être caché dans les sous-classes ...

class Dad
  {
    static String me = "dad";

    public void printMe ()
      {
        java.lang.reflect.Field field = this.getClass ().getDeclaredField ("me");
        System.out.println (field.get (null));
      }
  }
Sergey Ouchakov
la source
Très intéressant, Java.lang.reflect hein? ... +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();
}

... affichera "son".

user2895864
la source
n'est-ce pas la même que la question initiale?
Corley Brigman
Pouvez-vous expliquer pourquoi ? (dans votre réponse)
Mr_and_Mrs_D
3

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

Ça s'appelle Hiding Fields

À partir du lien ci-dessus

Au sein d'une classe, un champ qui porte le même nom qu'un champ de la superclasse masque le champ de la superclasse, même si leurs types sont différents. Dans la sous-classe, le champ de la superclasse ne peut pas être référencé par son nom simple. Au lieu de cela, le champ doit être accessible via super, qui est couvert dans la section suivante. De manière générale, nous ne recommandons pas de masquer les champs car cela rend le code difficile à lire.

sadjkas sadal
la source
1
Un lien vers une solution potentielle est toujours le bienvenu, mais veuillez ajouter du contexte autour du lien afin que vos collègues utilisateurs aient une idée de ce que c'est et pourquoi il est là. Citez toujours la partie la plus pertinente d'un lien important, au cas où le site cible serait inaccessible ou serait définitivement hors ligne. Tenez compte du fait qu'être à peine plus qu'un lien vers un site externe est une raison possible pour savoir pourquoi et comment certaines réponses sont-elles supprimées? .
FelixSFD
2

uniquement en remplaçant printMe():

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

la référence à medans la Dad.printMeméthode pointe implicitement vers le champ statique Dad.me, donc d'une manière ou d'une autre vous changez ce qui printMefait dans Son...

Dan Vinton
la source
2

Vous ne pouvez pas remplacer les variables d'une classe. Vous ne pouvez remplacer que les méthodes. Vous devriez garder les variables privées sinon vous pouvez avoir beaucoup de problèmes.

Ionel Bratianu
la source
Vous ne pouvez remplacer aucune statique.
Tom Hawtin - tackline
(ou du moins cela n'a pas de sens, IFSWIM.)
Tom Hawtin - tackline
Correct, vous ne pouvez pas remplacer la statique. Vous pouvez les OMBRER ou certaines personnes m'ont appelé MASKING.
Dale
2

Il imprime en effet «papa», puisque le champ n'est pas écrasé mais masqué. Il existe trois approches pour le faire imprimer 'fils':

Approche 1: remplacer 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();
}

Approche 2: ne pas masquer le champ et l'initialiser dans le constructeur

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

Approche 3: utiliser la valeur statique pour initialiser un champ dans le constructeur

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();
}
Rik Schaaf
la source
2

Les variables ne participent pas au remplacement. Seules les méthodes le font. Un appel de méthode est résolu au moment de l'exécution, c'est-à-dire que la décision d'appeler une méthode est prise au moment de l'exécution, mais les variables ne sont décidées qu'au moment de la compilation. Par conséquent, cette variable est appelée dont la référence est utilisée pour l'appel et non de l'objet d'exécution.

Jetez un œil à l'extrait suivant:

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

}

Lorsque vous exécutez le code, la console affichera:

Inside Honda
Inside Honda
Inside bike

Max_Speed = 150
Max_Speed = 90
Max_Speed = 90
Ananta SE
la source
0

Bien sûr, utiliser des attributs privés, des getters et des setters serait la chose recommandée à faire, mais j'ai testé ce qui suit, et cela fonctionne ... Voir le commentaire dans le code

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 ... ai-je juste redéfini les règles d'héritage ou ai-je mis Oracle dans une situation délicate? Pour moi, la chaîne statique protégée me est clairement remplacée, comme vous pouvez le voir lorsque vous exécutez ce programme. En outre, cela n'a aucun sens pour moi pourquoi les attributs ne devraient pas être remplaçables.

Pixel
la source
1
Dans ce cas, vous "cachez" la variable de la super classe. Ceci est distinct du remplacement et n'a rien à voir avec l'héritage. Les autres classes verront une variable ou une autre selon qu'elles référencent la classe de base ou la sous-classe et que celle qu'elles voient est fixée au moment de la compilation. Si vous changiez le nom "moi" dans la sous-classe en quelque chose d'autre, vous obtiendrez le même effet. La plupart des IDE et des outils de validation de code (findbugs, etc.) vous avertiront lorsque vous masquerez des variables comme celle-ci car ce n'est généralement pas ce que vous voulez faire.
AutomatedMike
Vous regardez la programmation uniquement du point de vue du codage. Ce qui est bien, codez comme vous le souhaitez dans ce cas. Si vous regardez le codage du point de vue d'un membre de l'équipe, les règles deviennent claires, par exemple pourquoi les champs ne peuvent pas être remplacés, etc. Je pourrais vous expliquer pourquoi, mais si vous ne le voyez pas du point de vue d'un membre de l'équipe, vous verrez simplement mes points comme invalides et me renvoyer des arguments invalides.
nckbrz
0

Pourquoi voudriez-vous remplacer les variables alors que vous pourriez facilement les réaffecter dans les sous-classes.

Je suis ce modèle pour travailler autour de la conception du langage. Supposons un cas où vous avez une classe de service lourde dans votre framework qui doit être utilisée dans différentes saveurs dans plusieurs applications dérivées.Dans ce cas, la meilleure façon de configurer la logique de la super classe est de réaffecter ses variables de `` définition ''.

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

}
Premganz
la source
0

Les variables de classe (également applicables aux variables d'instance) ne présentent pas de fonction de remplacement en Java car les variables de classe sont appelées sur la base du type d'objet appelant. Ajout d'une classe supplémentaire (Human) dans la hiérarchie pour la rendre plus claire. Alors maintenant nous avons

Fils étend papa étend l'humain

Dans le code ci-dessous, nous essayons d'itérer sur un tableau d'objets Humain, Papa et Fils, mais il imprime les valeurs de Classe Humaine dans tous les cas car le type d'objet appelant était Humain.

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

Imprimera

  • Humain
  • Humain
  • Humain

Donc, pas de remplacement des variables de classe.

Si nous voulons accéder à la variable de classe de l'objet réel à partir d'une variable de référence de sa classe parent, nous devons le dire explicitement au compilateur en convertissant la référence parent (objet humain) en son type.

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

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

Imprimera

  • papa
  • fils

Sur la partie de cette question: - Comme déjà suggéré, remplacez la méthode printMe () dans la classe Son, puis en appelant

Son().printMe();

La variable de classe de papa "me" sera masquée car la déclaration la plus proche (de la méthode printme () de la classe Son) du "moi" (dans la classe Son) aura la priorité.

Développeur
la source
0

Appelez simplement super.variable dans le constructeur de sous-classe

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

}

}

La sortie est de 10.

fractale80y
la source