L'histoire selon laquelle la méthode d'initialisation de variable appelée par le constructeur Java ne doit pas être remplacée

Parce qu'il était normalement utilisé dans une certaine bibliothèque Android.

problème

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

un événement

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 parFoo` 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 ".

Un piège caché dans l'initialisation des variables membres

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.

Définition des variables membres

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é.

Moment d'initialisation des variables de membre Java

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.

Description du problème

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é.

Supplément

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?

référence

(Il aurait dû être écrit officiellement, mais j'ai oublié le lien. Si je le trouve, je vais le coller ...)

Recommended Posts

L'histoire selon laquelle la méthode d'initialisation de variable appelée par le constructeur Java ne doit pas être remplacée
L'histoire selon laquelle le servlet n'a pas pu être chargé dans l'application Web Java
[Java] L'histoire selon laquelle le tableau attendu n'a pas été obtenu par la méthode String.split.
L'histoire selon laquelle la mise à jour forcée n'a pas pu être mise en œuvre
L'histoire que .java est également construite dans Unity 2018
Le cas où @Autowired n'a pas pu être utilisé dans JUnit5
[Gradle] L'histoire selon laquelle le fichier de classe n'existait pas dans le fichier jar
[Swift] Formes variables qui n'ont pas été enseignées dans le livre d'introduction
Appelez la super méthode en Java
Correspondant à "erreur que l'authentification de base ne réussit pas" dans le code de test "L'histoire qui n'a pas pu être faite"
J'ai essayé de résoudre les 10 dernières questions qui devraient être résolues après m'être inscrit auprès d'AtCoder en Java
Map keySet, les valeurs ne doivent pas être utilisées
L'histoire que je n'ai pas pu construire après l'installation de plusieurs Java sur Windows
Utilisez des modèles sur le chemin de classe avec Apache Velocity
L'histoire selon laquelle la méthode d'initialisation de variable appelée par le constructeur Java ne doit pas être remplacée
[Android] Solution lorsque l'appareil photo ne peut pas être démarré sur Android 9
[Java] Quand var doit être utilisé et quand il ne doit pas être utilisé
L'histoire selon laquelle la mise à jour forcée n'a pas pu être mise en œuvre
Quelle est la méthode principale en Java?
L'histoire de l'écriture de Java dans Emacs
À propos du cas où ("b" .. "aa") ne pourrait pas être utilisé dans Ruby Range
L'histoire que je n'ai pas pu construire après l'installation de plusieurs Java sur Windows
Je ne savais pas que les classes internes pouvaient être définies dans l'interface [Java]
[Java] Gestion des Java Beans dans la chaîne de méthodes
L'histoire de la fabrication d'un Othello ordinaire à Java
Une histoire sur le JDK à l'ère de Java 11
[Java] Obtenir KFunction à partir de la méthode / du constructeur en Java [Kotlin]
L'histoire du port devenant inutilisable dans l'exemple de programme Spring Boot
Principes de base de la gestion des erreurs Java - L'histoire qui capture n'est prise qu'au premier plan
[Java] Comment omettre le constructeur privé dans Lombok
Ecrire une classe qui peut être ordonnée en Java Un petit mémo standard
[Java] Quand var doit être utilisé et quand il ne doit pas être utilisé
[Java] Le rapport de couverture n'a pas pu être créé en combinant la méthode par défaut de l'interface Cobertura +
«Le serveur de prise en charge des langues pour Java a planté 5 fois au cours des 3 dernières minutes. Le serveur ne sera pas redémarré.»
Crier Java au cœur des technologies-Thèmes et technologies élémentaires que les ingénieurs Java devraient poursuivre en 2017-
L'histoire selon laquelle le paramètre de requête de l'application iPhone n'a pas pu être obtenu avec le servlet
[Java 8] Méthode de tri par ordre alphabétique et par ordre de longueur de chaîne de caractères pouvant être utilisée dans les tests de codage
[Java] Il semble que `0 <hoge <10` ne puisse pas être écrit dans l'expression conditionnelle de l'instruction ʻif`.