web-dev-qa-db-fra.com

Pourquoi this () et super () doivent-ils être la première déclaration d'un constructeur?

Java nécessite que si vous appelez this () ou super () dans un constructeur, ce soit la première instruction. Pourquoi?

Par exemple:

public class MyClass {
    public MyClass(int x) {}
}

public class MySubClass extends MyClass {
    public MySubClass(int a, int b) {
        int c = a + b;
        super(c);  // COMPILE ERROR
    }
}

Le compilateur Sun dit "l'appel de super doit être la première déclaration du constructeur". Le compilateur Eclipse indique "L'appel du constructeur doit être la première instruction d'un constructeur".

Cependant, vous pouvez contourner ce problème en réorganisant un peu le code:

public class MySubClass extends MyClass {
    public MySubClass(int a, int b) {
        super(a + b);  // OK
    }
}

Voici un autre exemple:

public class MyClass {
    public MyClass(List list) {}
}

public class MySubClassA extends MyClass {
    public MySubClassA(Object item) {
        // Create a list that contains the item, and pass the list to super
        List list = new ArrayList();
        list.add(item);
        super(list);  // COMPILE ERROR
    }
}

public class MySubClassB extends MyClass {
    public MySubClassB(Object item) {
        // Create a list that contains the item, and pass the list to super
        super(Arrays.asList(new Object[] { item }));  // OK
    }
}

Donc, c’est ne vous empêche pas d’exécuter la logique avant l’appel de super. Cela vous empêche simplement d'exécuter une logique impossible à intégrer dans une seule expression.

Il existe des règles similaires pour appeler this(). Le compilateur dit "l'appel à ceci doit être la première instruction du constructeur".

Pourquoi le compilateur a-t-il ces restrictions? Pouvez-vous donner un exemple de code où, si le compilateur n'avait pas cette restriction, quelque chose de mauvais arriverait?

559
Joe Daley

La classe parente 'constructor doit être appelée avant la sous-classe' constructor. Cela garantira que si vous appelez des méthodes de la classe parent dans votre constructeur, la classe parent a déjà été configurée correctement.

Ce que vous essayez de faire, transmettre des arguments au super constructeur est parfaitement légal. Il vous suffit de construire ces arguments en ligne comme vous le faites ou de les transmettre à votre constructeur, puis de les transmettre à super:

public MySubClassB extends MyClass {
        public MySubClassB(Object[] myArray) {
                super(myArray);
        }
}

Si le compilateur ne l’a pas appliqué, vous pouvez le faire:

public MySubClassB extends MyClass {
        public MySubClassB(Object[] myArray) {
                someMethodOnSuper(); //ERROR super not yet constructed
                super(myArray);
        }
}

Dans les cas où une classe parent a un constructeur par défaut, l'appel de super est inséré automatiquement pour vous par le compiler. Étant donné que chaque classe de Java hérite de Object, le constructeur d'objets doit être appelé d'une manière ou d'une autre et il doit d'abord être exécuté. L'insertion automatique de super () par le compilateur le permet. En obligeant super à apparaître en premier, les corps de constructeur sont exécutés dans le bon ordre, à savoir: Objet -> Parent -> Enfant -> ChildOfChild -> SoOnSoForth

174
anio

J'ai trouvé un moyen de contourner ce problème en enchaînant constructeurs et méthodes statiques. Ce que je voulais faire ressemblait à quelque chose comme ceci:

public class Foo extends Baz {
  private final Bar myBar;

  public Foo(String arg1, String arg2) {
    // ...
    // ... Some other stuff needed to construct a 'Bar'...
    // ...
    final Bar b = new Bar(arg1, arg2);
    super(b.baz()):
    myBar = b;
  }
}

Donc, fondamentalement, construisez un objet en fonction des paramètres du constructeur, stockez-le dans un membre et transmettez également le résultat d'une méthode sur cet objet au constructeur de super. Rendre le membre final était également assez important car la nature du cours est qu’il est immuable. Notez que comme cela se produit, la construction de Bar prend en réalité quelques objets intermédiaires, elle n'est donc pas réductible à une ligne dans mon cas d'utilisation réel.

J'ai fini par le faire fonctionner comme ceci:

public class Foo extends Baz {
  private final Bar myBar;

  private static Bar makeBar(String arg1,  String arg2) {
    // My more complicated setup routine to actually make 'Bar' goes here...
    return new Bar(arg1, arg2);
  }

  public Foo(String arg1, String arg2) {
    this(makeBar(arg1, arg2));
  }

  private Foo(Bar bar) {
    super(bar.baz());
    myBar = bar;
  }
}

Code légal, et il accomplit la tâche d’exécuter plusieurs instructions avant d’appeler le super constructeur.

93
pendor

Parce que le JLS le dit. Le JLS peut-il être modifié de manière compatible pour le permettre? Yup.

Cependant, cela compliquerait les spécifications linguistiques, qui sont déjà plus que compliquées. Ce ne serait pas une chose très utile à faire et il y a des moyens de le contourner (appeler un autre constructeur avec le résultat d'une méthode statique ou une expression lambda this(fn()) - la méthode est appelée avant l'autre constructeur, et donc aussi super constructeur). Le rapport poids/force du changement est donc défavorable.

Notez que cette règle seule n'empêche pas l'utilisation de champs avant la construction de la super classe.

Considérez ces exemples illégaux.

super(this.x = 5);

super(this.fn());

super(fn());

super(x);

super(this instanceof SubClass);
// this.getClass() would be /really/ useful sometimes.

Cet exemple est légal, mais "faux".

class MyBase {
    MyBase() {
        fn();
    }
    abstract void fn();
}
class MyDerived extends MyBase {
    void fn() {
       // ???
    }
}

Dans l'exemple ci-dessus, si MyDerived.fn requiert des arguments du constructeur MyDerived, il est nécessaire de les insérer avec un ThreadLocal. ; (

Incidemment, depuis Java 1.4, le champ synthétique contenant le this extérieur est affecté avant l'appel du super-constructeur de classes internes. Cela a provoqué des événements particuliers NullPointerException dans le code compilé pour cibler des versions antérieures.

Notez également que, en présence d'une publication dangereuse, la construction peut être visualisée à nouveau par d'autres threads, à moins que des précautions ne soient prises.

Edit Mars 2018: Dans le message Enregistrements: construction et validation Oracle suggère de supprimer cette restriction (mais contrairement à C #, this sera définitivement non affecté (DU) avant le chaînage du constructeur).

Historiquement, this () ou super () doivent figurer en premier dans un constructeur. Cette restriction n'a jamais été populaire et perçue comme arbitraire. Un certain nombre de raisons subtiles, y compris la vérification d'invokespecial, ont contribué à cette restriction. Au fil des ans, nous avons traité ces problèmes au niveau de la VM, au point qu'il devient pratique d'envisager de lever cette restriction, pas seulement pour les enregistrements, mais pour tous les constructeurs.

44

Je suis à peu près sûr (ceux qui sont familiers avec le carillon de spécification Java de) que cela vous empêche de (a) autoriser l'utilisation d'un objet partiellement construit, et (b), de forcer le constructeur de la classe parente construire sur un objet "frais".

Voici quelques exemples de "mauvaise" chose:

class Thing
{
    final int x;
    Thing(int x) { this.x = x; }
}

class Bad1 extends Thing
{
    final int z;
    Bad1(int x, int y)
    {
        this.z = this.x + this.y; // WHOOPS! x hasn't been set yet
        super(x);
    }        
}

class Bad2 extends Thing
{
    final int y;
    Bad2(int x, int y)
    {
        this.x = 33;
        this.y = y; 
        super(x); // WHOOPS! x is supposed to be final
    }        
}
13
Jason S

Tout simplement parce que c'est la philosophie de l'héritage. Et selon la spécification du langage Java, voici comment est défini le corps du constructeur:

ConstructorBody: {ExplicitConstructorInvocationopt BlockStatementsopt }

La première déclaration d'un constructeur peut être soit

  • une invocation explicite d'un autre constructeur de la même classe (en utilisant le mot clé "this"); ou
  • une invocation explicite de la superclasse directe (en utilisant le mot clé "super")

Si un corps de constructeur ne commence pas par une invocation explicite de constructeur et que le constructeur déclaré ne fait pas partie de la classe primordiale Object, le corps de constructeur commence implicitement par une invocation de constructeur de superclasse "super ();", invocation du constructeur de sa super-classe directe qui ne prend aucun argument. Et ainsi de suite ... toute une chaîne de constructeurs sera appelée jusqu'au constructeur d'Object; "Toutes les classes de la plate-forme Java sont des descendants d'objet". Cette chose s'appelle "Constructor Chaining".

Maintenant pourquoi est-ce?
Et la raison pour laquelle Java a défini le ConstructorBody de cette manière, c'est qu'ils devaient maintenir la hiérarchie de l'objet. Rappelez-vous la définition de l'héritage; C'est prolonger une classe. Cela dit, vous ne pouvez pas prolonger quelque chose qui n'existe pas. La base (la superclasse) doit être créée en premier, puis vous pouvez la dériver (la sous-classe). C'est pourquoi ils les ont appelés classes parent et enfant; vous ne pouvez pas avoir un enfant sans parent.

Sur le plan technique, une sous-classe hérite de tous les membres (champs, méthodes, classes imbriquées) de son parent. Et puisque les constructeurs ne sont PAS membres (ils n'appartiennent pas à des objets. Ils sont responsables de la création d'objets), ils ne sont donc pas hérités par les sous-classes, mais ils peuvent être invoqués. Et depuis au moment de la création de l'objet, UN SEUL constructeur est exécuté. Alors, comment pouvons-nous garantir la création de la super-classe lorsque vous créez l'objet de sous-classe? Ainsi, le concept de "chaînage de constructeur"; nous avons donc la possibilité d’appeler d’autres constructeurs (super) à partir du constructeur actuel. Et Java a requis que cet appel soit la PREMIÈRE ligne du constructeur de la sous-classe pour maintenir la hiérarchie et la garantir. Ils supposent que si vous ne créez pas explicitement l'objet parent FIRST (comme si vous l'aviez oublié), ils le feront implicitement pour vous.

Cette vérification est faite lors de la compilation. Mais je ne suis pas sûr de ce qui se passerait au moment de l'exécution, du type d'erreur d'exécution que nous aurions, SI Java ne génère pas d'erreur de compilation lorsque nous essayons explicitement d'exécuter un constructeur de base à partir de la sous-classe constructeur au milieu de son corps et non de la première ligne ...

12
Randa Sbeity

Vous avez demandé pourquoi, et les autres réponses, imo, ne dites pas vraiment pourquoi il est correct d'appeler le constructeur de votre super, mais seulement si c'est la toute première ligne. La raison est que vous n'êtes pas vraiment appelant le constructeur. En C++, la syntaxe équivalente est la suivante:

MySubClass: MyClass {

public:

 MySubClass(int a, int b): MyClass(a+b)
 {
 }

};

Lorsque vous voyez la clause initializer de cette manière, avant l’accolade ouverte, vous savez que c’est spécial. Il s'exécute avant le reste du constructeur et avant l'initialisation des variables membres. Ce n'est pas si différent pour Java. Il existe un moyen d'obtenir du code (d'autres constructeurs) à exécuter avant que le constructeur ne commence réellement, avant que les membres de la sous-classe ne soient initialisés. Et cela consiste à mettre "l'appel" (par exemple, super) sur la toute première ligne. (D'une certaine manière, super ou this est un peu avant la première accolade ouverte, même si vous la tapez après, car elle sera exécutée avant que tout soit complètement construit. ) Tout autre code après l'accolade ouverte (comme int c = a + b;) fait dire au compilateur "oh, ok, pas d'autres constructeurs, nous pouvons tout initialiser à ce moment-là." Ainsi, il s'exécute et initialise votre super classe et vos membres, entre autres choses, puis commence à exécuter le code après l'accolade ouverte.

Si, quelques lignes plus tard, il rencontre un code disant "oh ouais quand vous construisez cet objet, voici les paramètres que je veux que vous transmettiez au constructeur de la classe de base", c'est trop tard et ce n'est pas le cas. ne veut rien dire. Donc, vous obtenez une erreur de compilation.

9
Kate Gregory

Donc, cela ne vous empêche pas d'exécuter la logique avant l'appel à super. Cela vous empêche simplement d'exécuter une logique impossible à intégrer dans une seule expression.

En fait, vous pouvez exécuter une logique avec plusieurs expessions, il vous suffit de placer votre code dans une fonction statique et de l'appeler dans la super instruction.

En utilisant votre exemple:

public class MySubClassC extends MyClass {
    public MySubClassC(Object item) {
        // Create a list that contains the item, and pass the list to super
        super(createList(item));  // OK
    }

    private static List createList(item) {
        List list = new ArrayList();
        list.add(item);
        return list;
    }
}
6
Tip-Sy

Je suis tout à fait d’accord, les restrictions sont trop fortes. Utiliser une méthode d'assistance statique (comme suggéré par Tom Hawtin) ou insérer tous les "calculs pré-super ()" dans une seule expression dans le paramètre n'est pas toujours possible, par exemple:

class Sup {
    public Sup(final int x_) { 
        //cheap constructor 
    }
    public Sup(final Sup sup_) { 
        //expensive copy constructor 
    }
}

class Sub extends Sup {
    private int x;
    public Sub(final Sub aSub) {
        /* for aSub with aSub.x == 0, 
         * the expensive copy constructor is unnecessary:
         */

         /* if (aSub.x == 0) { 
          *    super(0);
          * } else {
          *    super(aSub);
          * } 
          * above gives error since if-construct before super() is not allowed.
          */

        /* super((aSub.x == 0) ? 0 : aSub); 
         * above gives error since the ?-operator's type is Object
         */

        super(aSub); // much slower :(  

        // further initialization of aSub
    }
}

Utiliser une exception "objet non encore construit", comme le suggérait Carson Myers, serait utile, mais le vérifier lors de la construction de chaque objet ralentirait l'exécution. Je préférerais un compilateur Java qui permette une meilleure différenciation (au lieu d'interdire inutilement une instruction if mais d'autoriser l'opérateur? Dans le paramètre), même si cela complique la spécification du langage.

4
DaveFar

J'ai trouvé un problème.

Cela ne compilera pas:

public class MySubClass extends MyClass {
    public MySubClass(int a, int b) {
        int c = a + b;
        super(c);  // COMPILE ERROR
        doSomething(c);
        doSomething2(a);
        doSomething3(b);
    }
}

Cela marche :

public class MySubClass extends MyClass {
    public MySubClass(int a, int b) {
        this(a + b);
        doSomething2(a);
        doSomething3(b);
    }

    private MySubClass(int c) {
        super(c);
        doSomething(c);
    }
}
3
Vouze

Pouvez-vous donner un exemple de code où, si le compilateur n'avait pas cette restriction, quelque chose de mauvais arriverait?

class Good {
    int essential1;
    int essential2;

    Good(int n) {
        if (n > 100)
            throw new IllegalArgumentException("n is too large!");
        essential1 = 1 / n;
        essential2 = n + 2;
    }
}

class Bad extends Good {
    Bad(int n) {
        try {
            super(n);
        } catch (Exception e) {
            // Exception is ignored
        }
    }

    public static void main(String[] args) {
        Bad b = new Bad(0);
//        b = new Bad(101);
        System.out.println(b.essential1 + b.essential2);
    }
}

Une exception lors de la construction indique presque toujours que l'objet en cours de construction n'a pas pu être initialisé correctement. Il est maintenant dans un état incorrect, inutilisable et doit être nettoyé. Cependant, un constructeur d'une sous-classe a la possibilité d'ignorer une exception survenue dans l'une de ses super-classes et de renvoyer un objet partiellement initialisé. Dans l'exemple ci-dessus, si l'argument donné à new Bad() est 0 ou supérieur à 100, ni essential1 ni essential2 ne sont correctement initialisés.

Vous pouvez dire qu'ignorer les exceptions est toujours une mauvaise idée. OK, voici un autre exemple:

class Bad extends Good {
    Bad(int n) {
        for (int i = 0; i < n; i++)
            super(i);
    }
}

C'est drôle, n'est ce pas? Combien d'objets créons-nous dans cet exemple? Un? Deux? Ou peut-être rien ...

Autoriser à appeler super() ou this() au milieu d'un constructeur ouvrirait une boîte de Pandora contenant des constructeurs odieux.


Par ailleurs, je comprends le besoin fréquent d'inclure une partie statique avant un appel à super() ou this(). Il peut s’agir de tout code ne s’appuyant pas sur la référence this (qui, en fait, existe déjà au tout début d’un constructeur, mais ne peut être utilisé dans l’ordre jusqu’à ce que super() ou this() retourne) et nécessaire pour faire un tel appel. De plus, comme dans toute méthode, il est possible que certaines variables locales créées avant l'appel de super() ou this() soient nécessaires.

Dans ce cas, vous avez les possibilités suivantes:

  1. Utilisez le motif présenté à cette réponse , ce qui permet de contourner la restriction.
  2. Attendez que l'équipe Java autorise les codes pré -super() et pre -this(). Cela peut être fait en imposant une restriction sur l'endroit où super() ou this() peut apparaître dans un constructeur. En fait, même le compilateur actuel est capable de distinguer les bons et les mauvais (ou potentiellement mauvais) cas avec le degré suffisant pour permettre en toute sécurité l'ajout de code statique au début d'un constructeur. En effet, supposons que super() et this() retournent this référence et, à son tour, votre constructeur a
return this;

à la fin. Ainsi que le compilateur rejette le code

public int get() {
    int x;
    for (int i = 0; i < 10; i++)
        x = i;
    return x;
}

public int get(int y) {
    int x;
    if (y > 0)
        x = y;
    return x;
}

public int get(boolean b) {
    int x;
    try {
        x = 1;
    } catch (Exception e) {
    }
    return x;
}

avec l'erreur "la variable x n'a peut-être pas été initialisée", il pourrait le faire sur la variable this, en effectuant ses vérifications comme pour toute autre variable locale. La seule différence est que this ne peut être attribué autrement que par super() ou this() appel (et, comme d'habitude, s'il n'y a pas un tel appel chez un constructeur, super() implicitement inséré par le compilateur au début) et peut ne pas être attribué deux fois. En cas de doute (comme dans le premier get(), où x est en fait toujours affecté), le compilateur peut renvoyer une erreur. Ce serait mieux que de simplement renvoyer une erreur sur n'importe quel constructeur où il y aurait autre chose qu'un commentaire avant super() ou this().

3
John McClane

J'imagine qu'ils ont agi de la sorte pour faciliter la vie des personnes qui écrivent des outils qui traitent du code Java et, dans une moindre mesure, des personnes qui lisent du code Java.

Si vous permettez à l'appel super() ou this() de se déplacer, plusieurs variantes doivent être vérifiées. Par exemple, si vous déplacez l'appel super() ou this() dans un if() conditionnel, il devra peut-être être assez intelligent pour insérer un super() implicite dans le else. Il peut être nécessaire de savoir comment signaler une erreur si vous appelez super() à deux reprises ou utilisez super() et this() ensemble. Il sera peut-être nécessaire d'interdire les appels de méthode sur le récepteur jusqu'à ce que super() ou this() soit appelé et déterminer à quel moment cela devient compliqué.

Faire en sorte que tout le monde fasse ce travail supplémentaire semblait probablement représenter un coût plus élevé que l’avantage.

3
Dobes Vandermeer

Vous pouvez utiliser des blocs d'initialisation anonymes pour initialiser les champs de l'enfant avant d'appeler son constructeur. Cet exemple démontrera:

public class Test {
    public static void main(String[] args) {
        new Child();
    }
}

class Parent {
    public Parent() {
        System.out.println("In parent");
    }
}

class Child extends Parent {

    {
        System.out.println("In initializer");
    }

    public Child() {
        super();
        System.out.println("In child");
    }
}

Cela produira:

En parent
Dans l'initialiseur
Chez l'enfant

2
Savvas Dalkitsis

Il est logique que les constructeurs terminent leur exécution dans l'ordre de leur dérivation. Étant donné qu’une super-classe n’a connaissance d’aucune sous-classe, toute initialisation qu’elle doit exécuter est distincte et peut-être préalable à toute initialisation effectuée par la sous-classe. Par conséquent, il doit d'abord terminer son exécution.

Une démonstration simple:

class A {
    A() {
        System.out.println("Inside A's constructor.");
    }
}

class B extends A {
    B() {
        System.out.println("Inside B's constructor.");
    }
}

class C extends B {
    C() {
        System.out.println("Inside C's constructor.");
    }
}

class CallingCons {
    public static void main(String args[]) {
        C c = new C();
    }
}

La sortie de ce programme est:

Inside A's constructor
Inside B's constructor
Inside C's constructor
2
Jaydev

En fait, super() est la première instruction d'un constructeur car elle permet de s'assurer que sa super-classe est entièrement formée avant la construction de la sous-classe. Même si vous n'avez pas super() dans votre première instruction, le compilateur l'ajoutera pour vous!

1

C'est parce que votre constructeur dépend d'autres constructeurs. Pour que votre constructeur fonctionne correctement, il est nécessaire que d’autres constructeurs dépendent correctement. C'est pourquoi il est nécessaire de vérifier d'abord les constructeurs dépendants qui ont appelé soit par this (), soit par super () dans votre constructeur. Si d'autres constructeurs qui ont appelé soit par this () ou par super () ont un problème, exécutez d'autres instructions car toutes échoueront si le constructeur appelé échoue.

1
Farrukh Ahmed

Je sais que je suis un peu en retard à la fête, mais j'ai déjà utilisé cette astuce à quelques reprises (et je sais que c'est un peu inhabituel):

Je crée une interface générique InfoRunnable<T> avec une méthode:

public T run(Object... args);

Et si je dois faire quelque chose avant de le transmettre au constructeur, je fais simplement ceci:

super(new InfoRunnable<ThingToPass>() {
    public ThingToPass run(Object... args) {
        /* do your things here */
    }
}.run(/* args here */));
1
mid

Tldr:

Les autres réponses ont abordé le "pourquoi" de la question. Je fournirai un hack autour de cette limitation:

L'idée de base est de pirater l'instruction super avec vos instructions incorporées. Cela peut être fait en déguisant vos déclarations en ) expressions .

Tsdr:

Considérons que nous voulons faire Statement1() à Statement9() avant d'appeler super():

public class Child extends Parent {
    public Child(T1 _1, T2 _2, T3 _3) {
        Statement_1();
        Statement_2();
        Statement_3(); // and etc...
        Statement_9();
        super(_1, _2, _3); // compiler rejects because this is not the first line
    }
}

Le compilateur rejettera bien sûr notre code. Donc, au lieu de cela, nous pouvons faire ceci:

// This compiles fine:

public class Child extends Parent {
    public Child(T1 _1, T2 _2, T3 _3) {
        super(F(_1), _2, _3);
    }

    public static T1 F(T1 _1) {
        Statement_1();
        Statement_2();
        Statement_3(); // and etc...
        Statement_9();
        return _1;
    }
}

La seule limitation est que la classe parent doit avoir un constructeur qui prend au moins un argument afin que nous puissions faufiler notre déclaration en tant qu'expression.

Voici un exemple plus élaboré:

public class Child extends Parent {
    public Child(int i, String s, T1 t1) {
        i = i * 10 - 123;
        if (s.length() > i) {
            s = "This is substr s: " + s.substring(0, 5);
        } else {
            s = "Asdfg";
        }
        t1.Set(i);
        T2 t2 = t1.Get();
        t2.F();
        Object obj = Static_Class.A_Static_Method(i, s, t1);
        super(obj, i, "some argument", s, t1, t2); // compiler rejects because this is not the first line
    }
}

Retravaillé en:

// This compiles fine:

public class Child extends Parent {
    public Child(int i, String s, T1 t1) {
        super(Arg1(i, s, t1), Arg2(i), "some argument", Arg4(i, s), t1, Arg6(i, t1));
    }

    private static Object Arg1(int i, String s, T1 t1) {
        i = Arg2(i);
        s = Arg4(s);
        return Static_Class.A_Static_Method(i, s, t1);
    }

    private static int Arg2(int i) {
        i = i * 10 - 123;
        return i;
    }

    private static String Arg4(int i, String s) {
        i = Arg2(i);
        if (s.length() > i) {
            s = "This is sub s: " + s.substring(0, 5);
        } else {
            s = "Asdfg";
        }
        return s;
    }

    private static T2 Arg6(int i, T1 t1) {
        i = Arg2(i);
        t1.Set(i);
        T2 t2 = t1.Get();
        t2.F();
        return t2;
    }
}

En fait, les compilateurs auraient pu automatiser ce processus pour nous. Ils avaient juste choisi de ne pas le faire.

0
Pacerier

Avant de pouvoir construire un objet enfant, votre objet parent doit être créé. Comme vous le savez quand vous écrivez un cours comme ceci:

public MyClass {
        public MyClass(String someArg) {
                System.out.println(someArg);
        }
}

il se tourne vers le suivant (prolonger et super sont juste cachés):

public MyClass extends Object{
        public MyClass(String someArg) {
                super();
                System.out.println(someArg);
        }
}

Nous créons d’abord un Object, puis nous étendons cet objet à MyClass. Nous ne pouvons pas créer MyClass avant le Object. La règle simple est que le constructeur du parent doit être appelé avant le constructeur enfant. Mais nous savons que les classes peuvent avoir plus d’un constructeur. Java nous permet de choisir un constructeur qui sera appelé (ou bien ce sera super() ou super(yourArgs...)). Ainsi, lorsque vous écrivez super(yourArgs...), vous redéfinissez le constructeur qui sera appelé pour créer un objet parent. Vous ne pouvez pas exécuter d'autres méthodes avant super() parce que l'objet n'existe pas encore (mais après super(), un objet sera créé et vous pourrez faire tout ce que vous voulez).

Alors pourquoi ne pouvons-nous pas exécuter this() après une méthode? Comme vous le savez, this() est le constructeur de la classe actuelle. Nous pouvons aussi avoir un nombre différent de constructeurs dans notre classe et les appeler comme this() ou this(yourArgs...). Comme je l'ai dit, chaque constructeur a caché la méthode super(). Lorsque nous écrivons notre super(yourArgs...) personnalisée, nous supprimons super() avec super(yourArgs...). De même, lorsque nous définissons this() ou this(yourArgs...), nous supprimons également notre super() dans le constructeur actuel, car si super() était associé à this() dans la même méthode, il en créerait davantage. puis un objet parent. C'est pourquoi les mêmes règles sont imposées pour la méthode this(). Il retransmet simplement la création d'objet parent vers un autre constructeur enfant et ce constructeur appelle le constructeur super() pour la création parent. Donc, le code sera comme ça en fait:

public MyClass extends Object{
        public MyClass(int a) {
                super();
                System.out.println(a);
        }
        public MyClass(int a, int b) {
                this(a);
                System.out.println(b);
        }
}

Comme d’autres disent, vous pouvez exécuter du code comme ceci:

this(a+b);

aussi vous pouvez exécuter du code comme ceci:

public MyClass(int a, SomeObject someObject) {
    this(someObject.add(a+5));
}

Mais vous ne pouvez pas exécuter de code comme ceci car votre méthode n'existe pas encore:

public MyClass extends Object{
    public MyClass(int a) {

    }
    public MyClass(int a, int b) {
        this(add(a, b));
    }
    public int add(int a, int b){
        return a+b;
    }
}

De plus, vous êtes obligé d'avoir le constructeur super() dans votre chaîne de méthodes this(). Vous ne pouvez pas créer un objet comme celui-ci:

public MyClass{
        public MyClass(int a) {
                this(a, 5);
        }
        public MyClass(int a, int b) {
                this(a);
        }
}
0
Alexandr
class C
{
    int y,z;

    C()
    {
        y=10;
    }

    C(int x)
    {
        C();
        z=x+y;
        System.out.println(z);
    }
}

class A
{
    public static void main(String a[])
    {
        new C(10);
    }
}

Voir l'exemple si nous appelons le constructeur C(int x) alors la valeur de z dépend de y si nous n'appelons pas C() dans la première ligne, ce sera le problème pour z. z ne pourrait pas obtenir la valeur correcte.

0
PANKAJ KUMAR