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 imprimera "papa". Y at-il un moyen de le faire imprimer "fils"?
Oui. Mais en ce qui concerne la variable, il s'agit d'un écrasement (attribution d'une nouvelle valeur à une variable. L'attribution d'une nouvelle définition à la fonction est Override) .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 parente
si la variable est statique, 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. Ça va se refléter en super classe
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 cachez. La substitution est par exemple une méthode. La dissimulation est différente de la priorité.
Dans l'exemple que vous avez donné, en déclarant la variable de classe portant le nom "me" dans la classe Son, vous masquez la variable de classe qu'elle aurait héritée de la superclasse Papa portant le même nom "moi". Cacher une variable de cette manière n'affecte pas la valeur de la variable de classe 'me' dans la super-classe Papa.
Pour la deuxième partie de votre question, sur la façon de faire imprimer "son", je définirais la valeur via le constructeur. Bien que le code ci-dessous s'écarte beaucoup de votre question initiale, je l'écrirais à peu près 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 la dissimulation dans la section 8.3 - Déclarations sur le terrain
Oui, il suffit de remplacer la méthode printMe()
:
class Son extends Dad {
public static final String me = "son";
@Override
public void printMe() {
System.out.println(me);
}
}
Vous pouvez créer un getter et ensuite remplacer ce getter. Cela est particulièrement utile si la variable que vous substituez est une sous-classe en soi. Imaginez que votre super-classe ait un membre Object
mais que, dans votre sous-classe, il est maintenant plus défini comme étant 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"
}
Si vous voulez le remplacer, je ne vois pas de raison valable pour garder cette valeur statique. Je suggérerais l'utilisation de l'abstraction (voir le code exemple). :
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());
}
}
}
Sortie System.out: Tommy Nancy Dad
System.err est le même que ci-dessus (a juste la police rouge)
JOption Sortie:
Tommy alors
Nancy alors
Papa
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.
Bien qu'il soit vrai que les variables de classe ne puissent être masquées que dans des 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 me
comme protected
ne semble pas avoir beaucoup de sens dans ce contexte, car il va être caché dans des 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));
}
}
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();
}
... va imprimer "fils".
uniquement en remplaçant printMe()
:
class Son extends Dad
{
public void printMe()
{
System.out.println("son");
}
}
la référence à me
dans la méthode Dad.printMe
pointe implicitement vers le champ statique Dad.me
; vous modifiez donc d'une manière ou d'une autre l'action de printMe
dans Son
...
Vous ne pouvez pas écraser des variables dans une classe. Vous pouvez remplacer uniquement les méthodes. Vous devez garder les variables privées, sinon vous pouvez avoir beaucoup de problèmes.
Il affiche en effet «papa», puisque le champ n’est pas remplacé mais caché. 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 masque pas le champ et l'initialise 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();
}
https://docs.Oracle.com/javase/tutorial/Java/IandI/hidevariables.html
C'est ce qu'on appelle des champs cachés
À partir du lien ci-dessus
Dans une classe, un champ portant 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 super-classe 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.
Les variables ne participent pas à la substitution. Seules les méthodes 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 sont décidées au moment de la compilation uniquement. 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 coup d'œ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
Bien sûr, en utilisant des attributs privés, des getters et des setters seraient recommandés, mais j’ai testé ce qui suit, et ça marche ... 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 ... est-ce que je viens de redéfinir les règles d'héritage ou est-ce que j'ai mis Oracle dans une situation délicate? De plus, cela n’a aucun sens pour moi pourquoi les attributs ne devraient pas être remplaçables.
Pourquoi voudriez-vous remplacer les variables alors que vous pourriez facilement les réaffecter dans les sous-classes.
Je suis ce modèle pour contourner la conception de la langue. Supposons que votre infrastructure comporte une classe de service lourde qui doit être utilisée de différentes manières dans plusieurs applications dérivées. Dans ce cas, le meilleur moyen de configurer la logique de la super-classe consiste à 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";
}
}
Non. Les variables de classe (ne s'appliquent pas non plus aux variables d'instance) ne présentent pas de fonctionnalité dominante en Java, car les variables de classe sont appelées en fonction du type d'objet appelant. Ajout d'une classe supplémentaire (Humain) dans la hiérarchie pour la rendre plus claire. Alors maintenant nous avons
Fils étend papa étend humain
Dans le code ci-dessous, nous essayons d’itérer sur un tableau d’objets Humain, Papa et Fils, mais il affiche les valeurs de Human Class dans tous les cas, car le type de l’appelant était 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
}
}
}
Va imprimer
Donc, pas de substitution de variables de classe.
Si nous voulons accéder à la variable de classe d'un objet réel à partir d'une variable de référence de sa classe parente, nous devons l'indiquer explicitement au compilateur en convertissant la référence parente (objet humain) dans son type.
System.out.println(((Dad)humans[1]).me); // prints dad
System.out.println(((Son)humans[2]).me); // prints son
Va imprimer
Sur la manière dont une partie de cette question: - Comme déjà suggéré, remplacez la méthode printMe () dans la classe Son, puis appelez
Son().printMe();
La variable de classe "me" de papa sera masquée car la déclaration la plus proche (de la méthode printme () de la classe Son) du "me" (dans la classe Son) aura la priorité.