Parce qu'il était normalement utilisé dans une certaine bibliothèque Android.
Soudain, voyez-vous la sortie de l'implémentation suivante?
public class Main {
static class Foo {
public Foo() {
init();
}
protected void init() {
}
}
static class Bar extends Foo {
private int val1;
private int val2 = 0;
@Override
protected void init() {
val1 = 1;
val2 = 2;
}
}
public static void main(String... args) {
Bar bar = new Bar();
System.out.println(bar.val1);
System.out.println(bar.val2);
}
}
Le résultat de sortie est le suivant.
1
0
La classe Foo
est un constructeur qui appelle la méthode ʻinit ()`.
La classe Bar
est une sous-classe de Foo
, remplaçant la méthode ʻinit ()créée par
Foo` pour initialiser les variables membres.
Dans «Bar.init ()», les variables membres «val1» et «val2» sont respectivement attribuées «1» et «2».
Si vous instanciez cette classe Bar
et sortez chaque valeur, val1
contient 1
comme vous pouvez le voir, mais la valeur de val2
qui aurait dû être affectée à 2
est` 0 C'est ".
Il y a deux clés pour démêler ce phénomène.
L'un est la définition des variables membres et l'autre est la synchronisation d'initialisation des variables en Java.
Dans le code en question, les variables membres de la classe Bar
sont définies comme suit:
Bar
private int val1;
private int val2 = 0;
En ce qui concerne la définition des variables membres, «0» est attribué pour les primitives et «null» pour les classes est attribué si aucune initialisation explicite n'est effectuée. Cependant, même s'il est initialisé avec «0», son comportement change selon qu'il est ou non explicitement initialisé.
En Java, l'initialisation des variables membres est effectuée dans le constructeur. La question est de savoir quand cela se produit dans le constructeur.
Comme je pense que je l'écris habituellement avec désinvolture, l'initialisation des variables membres se fait en fait ** immédiatement après avoir appelé le constructeur de la classe parent **.
En d'autres termes
Foo
static class Foo {
public Foo() {
← ici
init();
}
}
est.
Je ne pense pas que vous puissiez voir quoi que ce soit, mais les constructeurs Java nécessitent ** d'appeler le constructeur de la classe parente au début du constructeur **. Cependant, il existe une condition qui peut être omise, à savoir ** la classe parente définit le constructeur par défaut **. Le constructeur par défaut est un constructeur qui ne prend aucun argument. (Strictement parlant, ce n'est peut-être pas le constructeur parent, mais je vais l'omettre.)
Donc, dans le code ci-dessus, c'est la même chose que d'avoir un appel super ()
à la position de la flèche.
Sur la base de ce qui précède, je vais expliquer le piège sur lequel j'ai marché dans le code en question.
C'est la classe «Bar» qui a marché sur le piège.
Bar
static class Bar extends Foo {
private int val1;
private int val2 = 0;
@Override
protected void init() {
val1 = 1;
val2 = 2;
}
}
Si vous écrivez cette classe sans l'omettre, ce sera comme suit.
Bar
static class Bar extends Foo {
private int val1;
private int val2 = 0;
public Bar() {
super();
}
@Override
protected void init() {
val1 = 1;
val2 = 2;
}
}
Le constructeur Bar
appelle super ()
, c'est-à-dire le constructeur Foo
.
Le constructeur Foo
appelle ʻinit (), qui est une substitution de la classe
Bar, donc c'est un appel à
Bar.init ()`.
Si vous étendez ce flux à peu près
Bar() {
super() {
Bar.init() {
Bar.val1 = 1;
Bar.val2 = 2;
}
}
Initialisation des variables des membres Bar() {
Bar.val2 = 0;
}
}
Ce sera le flux.
«Val1» n'est-il pas initialisé avec «0» après «super ()»? Vous pouvez penser que, s'il n'est pas initialisé au moment de la définition, le processus d'initialisation lui-même après super ()
ne sera pas exécuté.
Au fait, si vous faites référence à «val2» de «Bar.init ()» avant d'attribuer une valeur, «0» sera renvoyé. Imaginons que toutes les variables membres se voient attribuer «0» ou «null» quelle que soit l'initialisation de la définition de membre, et que la valeur spécifiée après «super ()» soit attribuée.
En premier lieu, la première implémentation que j'ai publiée est le code de la bouse, alors pourquoi ne pas l'utiliser après avoir compris l'héritage?
(Il aurait dû être écrit officiellement, mais j'ai oublié le lien. Si je le trouve, je vais le coller ...)
Recommended Posts