Waarom moeten this() en super() het eerste statement in een constructor zijn?

Java vereist dat als je this() of super() aanroept in een constructor, dit de eerste instructie moet zijn. Waarom?

Bijvoorbeeld:

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

De Sun-compiler zegt “aanroep naar super moet de eerste instructie in de constructor zijn”. De Eclipse-compiler zegt “Constructor-aanroep moet het eerste statement in een constructor zijn”.

Je kunt dit echter omzeilen door de code een beetje te herschikken:

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

Hier is nog een voorbeeld:

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

Dus het houdt je niet tegen om logica uit te voeren vóór de aanroep naar super. Het weerhoudt je er alleen van om logica uit te voeren die niet in een enkele uitdrukking past.

Er zijn vergelijkbare regels voor het aanroepen van this(). De compiler zegt “aanroep naar dit moet de eerste instructie in de constructor zijn”.

Waarom heeft de compiler deze beperkingen? Kun je een codevoorbeeld geven waarin, als de compiler deze beperking niet had, er iets ergs zou gebeuren?


Antwoord 1, autoriteit 100%

De constructor van de bovenliggende klasse moet worden aangeroepen vóór de constructor van de subklasse. Dit zorgt ervoor dat als u methoden aanroept op de bovenliggende klasse in uw constructor, de bovenliggende klasse al correct is ingesteld.

Wat u probeert te doen, argumenten doorgeven aan de superconstructor is volkomen legaal, u hoeft alleen die argumenten inline te construeren terwijl u bezig bent, of ze aan uw constructor door te geven en ze vervolgens door te geven aan super:

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

Als de compiler dit niet heeft afgedwongen, kunt u dit doen:

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

In gevallen waarin een parent-klasse een standaardconstructor heeft, wordt de aanroep naar super automatisch voor je ingevoegd door de compiler. Aangezien elke klasse in Java erft van Object, moet de constructor van objecten op de een of andere manier worden aangeroepen en moet deze eerst worden uitgevoerd. Het automatisch invoegen van super() door de compiler maakt dit mogelijk. Super dwingen om als eerste te verschijnen, dwingt dat constructor-instanties in de juiste volgorde worden uitgevoerd, wat zou zijn: Object -> Ouder -> Kind -> ChildOfChild -> SoOnSoForth


Antwoord 2, autoriteit 54%

Ik heb een manier gevonden om dit te omzeilen door constructors en statische methoden aan elkaar te koppelen. Wat ik wilde doen zag er ongeveer zo uit:

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

Construeer dus in feite een object op basis van constructorparameters, sla het object op in een lid en geef ook het resultaat van een methode op dat object door aan de constructor van super. Het lidmaatschappelijk maken was ook redelijk belangrijk omdat de aard van de klas onveranderlijk is. Merk op dat het construeren van Bar eigenlijk een paar tussenliggende objecten vereist, dus het is niet te herleiden tot een one-liner in mijn werkelijke gebruik.

Ik heb het uiteindelijk zo laten werken:

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

Juridische code, en het volbrengt de taak van het uitvoeren van meerdere instructies voordat de superconstructor wordt aangeroepen.


Antwoord 3, autoriteit 27%

Omdat de JLS dat zegt. Kan de JLS op een compatibele manier worden gewijzigd om dit toe te staan? Ja.

Het zou echter de taalspecificatie compliceren, die al meer dan ingewikkeld genoeg is. Het zou niet erg handig zijn om te doen en er zijn manieren om het te omzeilen (roep een andere constructor aan met het resultaat van een statische methode of lambda-expressie this(fn()) – de methode wordt eerder aangeroepen de andere constructor, en dus ook de superconstructor). Dus de verhouding tussen vermogen en gewicht om de verandering uit te voeren is ongunstig.

Merk op dat deze regel alleen het gebruik van velden niet verhindert voordat de superklasse de constructie heeft voltooid.

Denk eens aan deze illegale voorbeelden.

super(this.x = 5);
super(this.fn());
super(fn());
super(x);
super(this instanceof SubClass);
// this.getClass() would be /really/ useful sometimes.

Dit voorbeeld is legaal, maar “fout”.

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

In het bovenstaande voorbeeld, als MyDerived.fn argumenten van de MyDerived-constructor vereiste, zouden ze moeten worden doorgesluisd met een ThreadLocal. ;(

Overigens wordt sinds Java 1.4 het synthetische veld dat de buitenste this bevat, toegewezen voordat de superconstructor van de binnenste klassen wordt aangeroepen. Dit veroorzaakte eigenaardige NullPointerException-gebeurtenissen in code die was gecompileerd om eerdere versies te targeten.

Houd er rekening mee dat als er sprake is van onveilige publicaties, de constructie kan worden bekeken in een andere volgorde door andere threads, tenzij er voorzorgsmaatregelen worden genomen.

Bewerk maart 2018: In bericht Records: constructie en validatie Oracle stelt voor deze beperking te verwijderen (maar in tegenstelling tot C#, zal this absoluut niet-toegewezen (DU) zijn vóór de constructor ketenen).

Historisch gezien moet this() of super() de eerste zijn in een constructor. Deze
beperking was nooit populair en werd als willekeurig ervaren. Er waren
een aantal subtiele redenen, waaronder de verificatie van
invokespecial, die heeft bijgedragen aan deze beperking. Door de jaren heen,
we hebben deze aangepakt op VM-niveau, tot het punt waarop het wordt
praktisch om te overwegen deze beperking op te heffen, niet alleen voor records,
maar voor alle constructeurs.


Antwoord 4, autoriteit 9%

Gewoon omdat dit de overervingsfilosofie is. En volgens de Java-taalspecificatie is dit hoe de body van de constructor wordt gedefinieerd:

ConstructorBody:
{ ExplicitConstructorInvocationopt    BlockStatementsopt }

De eerste instructie van een constructortekst kan zijn:

  • een expliciete aanroep van een andere constructor van dezelfde klasse (door het trefwoord “this” te gebruiken); of
  • een expliciete aanroep van de directe superklasse (met het trefwoord “super”)

Als een constructor-body niet begint met een expliciete constructor-aanroep en de constructor die wordt gedeclareerd geen deel uitmaakt van de primordiale klasse Object, dan begint de constructor-body impliciet met een superclass-constructor-aanroep “super();”, een aanroep van de constructor van zijn directe superklasse die geen argumenten aanneemt. En zo verder.. er zal een hele keten van constructors zijn die helemaal terug naar de constructor van Object worden genoemd; “Alle klassen in het Java-platform zijn afstammelingen van Object”. Dit ding heet “Constructor Chaining“.

Waarom is dit?
En de reden waarom Java de ConstructorBody op deze manier definieerde, is dat ze de hiërarchie van het object moesten behouden. Denk aan de definitie van de erfenis; Het verlengt een klas. Dat gezegd hebbende, je kunt iets dat niet bestaat niet uitbreiden. De basis (de superklasse) moet eerst worden gemaakt, daarna kun je deze afleiden (de subklasse). Daarom noemden ze ze ouder- en kindklassen; je kunt geen kind krijgen zonder een ouder.

Op technisch niveau erft een subklasse alle leden (velden, methoden, geneste klassen) van zijn bovenliggende klasse. En aangezien Constructors GEEN leden zijn (ze behoren niet tot objecten. Ze zijn verantwoordelijk voor het maken van objecten), dus ze worden NIET geërfd door subklassen, maar ze kunnen worden aangeroepen. En aangezien op het moment van het maken van het object slechts EEN constructor wordt uitgevoerd. Dus hoe garanderen we de creatie van de superklasse wanneer je het subklasse-object maakt? Dus het concept van “constructor chaining”; dus we hebben de mogelijkheid om andere constructors (d.w.z. super) aan te roepen vanuit de huidige constructor. En Java vereiste dat deze aanroep de EERSTE regel in de subklasseconstructor was om de hiërarchie te behouden en te garanderen. Ze gaan ervan uit dat als je het bovenliggende object niet EERST maakt (zoals als je het vergeten bent), ze het impliciet voor je doen.

Deze controle wordt gedaan tijdens het compileren. Maar ik weet niet zeker wat er zou gebeuren tijdens runtime, wat voor soort runtime-fout we zouden krijgen, ALS Java geen compileerfout genereert wanneer we expliciet proberen een basisconstructor uit te voeren vanuit de constructor van een subklasse in het midden van zijn body en niet vanaf de allereerste regel …


Antwoord 5, autoriteit 7%

Ik ben er vrij zeker van (degenen die bekend zijn met de Java-specificatie roepen in) dat het is om te voorkomen dat u (a) een gedeeltelijk geconstrueerd object mag gebruiken, en (b), de constructor van de bovenliggende klasse dwingt om te construeren op een “vers” object.

Enkele voorbeelden van een “slechte” zaak zijn:

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

Antwoord 6, autoriteit 5%

Je vroeg waarom, en de andere antwoorden, imo, zeggen niet echt waarom het oké is om de constructor van je super te bellen, maar alleen als het de allereerste regel is. De reden is dat je de constructor niet echt aanroept. In C++ is de equivalente syntaxis

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

Als je de initialisatieclausule op zichzelf zo ziet, vóór de open accolade, weet je dat het speciaal is. Het wordt uitgevoerd voordat een van de rest van de constructor wordt uitgevoerd en in feite voordat een van de lidvariabelen wordt geïnitialiseerd. Voor Java is het niet zo anders. Er is een manier om code (andere constructors) te laten lopen voordat de constructor echt begint, voordat leden van de subklasse worden geïnitialiseerd. En die manier is om de “call” (bijv. super) op de allereerste regel te zetten. (In zekere zin is die super of this een beetje voor de eerste open accolade, ook al typ je het daarna, omdat het wordt uitgevoerd voordat je ter zake komt dat alles volledig is geconstrueerd.) Elke andere code na de open accolade (zoals int c = a + b;) laat de compiler zeggen: “oh, ok, geen andere constructors, we kunnen dan alles initialiseren. ” Dus het loopt weg en initialiseert je superklasse en je leden en zo en begint dan de code uit te voeren na de open accolade.

Als het een paar regels later een code tegenkomt die zegt: “oh ja, als je dit object construeert, hier zijn de parameters die ik wil dat je doorgeeft aan de constructor voor de basisklasse”, is het te laat en het heeft geen zin. U krijgt dus een compilerfout.


Antwoord 7, autoriteit 3%

Het weerhoudt u er dus niet van om logica uit te voeren vóór de aanroep naar
Super. Het weerhoudt je er gewoon van om logica uit te voeren die je niet kunt passen
in een enkele uitdrukking.

Eigenlijk kun je logica uitvoeren met verschillende expressies, je hoeft alleen je code in een statische functie te stoppen en deze in de super-instructie aan te roepen.

Uw voorbeeld gebruiken:

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

Antwoord 8, autoriteit 2%

Ik ben het er helemaal mee eens, de beperkingen zijn te streng. Het is niet altijd mogelijk om een ​​statische hulpmethode te gebruiken (zoals Tom Hawtin – tackline suggereerde) of alle “pre-super() berekeningen” in een enkele uitdrukking in de parameter te schuiven, bijvoorbeeld:

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

Het gebruik van een uitzondering “object nog niet geconstrueerd”, zoals Carson Myers suggereerde, zou helpen, maar het controleren hiervan tijdens elke objectconstructie zou de uitvoering vertragen. Ik zou de voorkeur geven aan een Java-compiler die een betere differentiatie maakt (in plaats van een if-statement te verbieden maar de ?-operator binnen de parameter toe te staan), zelfs als dit de taalspecificatie compliceert.


Antwoord 9, autoriteit 2%

Ik heb een oplossing gevonden.

Dit compileert niet:

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

Dit werkt:

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

Antwoord 10, autoriteit 2%

Mijn gok is dat ze dit deden om het leven van mensen die tools schrijven die Java-code verwerken gemakkelijker te maken, en in mindere mate ook voor mensen die Java-code lezen.

Als je de aanroep super() of this() laat bewegen, zijn er meer variaties om op te controleren. Als u bijvoorbeeld de aanroep super() of this() naar een voorwaardelijke if() verplaatst, moet het misschien slim genoeg zijn om in te voegen een impliciete super() in de else. Het moet mogelijk weten hoe een fout moet worden gerapporteerd als u super() twee keer aanroept, of super() en this() samen gebruikt. Het kan nodig zijn om methodeaanroepen op de ontvanger niet toe te staan ​​totdat super() of this() wordt aangeroepen en uitzoeken wanneer dat ingewikkeld wordt.

Iedereen dit extra werk laten doen leek waarschijnlijk meer kosten dan baten.


Antwoord 11, autoriteit 2%

Kun je een codevoorbeeld geven waarin, als de compiler deze beperking niet had, er iets ergs zou gebeuren?

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

Een uitzondering tijdens de constructie geeft bijna altijd aan dat het object dat wordt geconstrueerd niet correct kon worden geïnitialiseerd, zich nu in een slechte staat bevindt, onbruikbaar is en moet worden verzameld. Een constructor van een subklasse heeft echter de mogelijkheid om een ​​uitzondering in een van zijn superklassen te negeren en een gedeeltelijk geïnitialiseerd object te retourneren. Als in het bovenstaande voorbeeld het argument voor new Bad() 0 of groter is dan 100, dan zijn noch essential1 noch essential2 correct geïnitialiseerd.

Je zou kunnen zeggen dat het negeren van uitzonderingen altijd een slecht idee is. OK, hier is nog een voorbeeld:

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

Grappig, niet? Hoeveel objecten maken we in dit voorbeeld? Een? Twee? Of misschien niets…

Als u super() of this() in het midden van een constructor zou aanroepen, zou er een doos van Pandora met gruwelijke constructeurs openen.


Aan de andere kant begrijp ik dat het vaak nodig is om een ​​statisch deel op te nemen voor een aanroep naar super() of this(). Dit kan elke code zijn die niet vertrouwt op this-referentie (die in feite al helemaal aan het begin van een constructor bestaat, maar niet ordelijk kan worden gebruikt tot super() of this() retourneert) en was nodig om een ​​dergelijke oproep te doen. Bovendien is er, zoals bij elke methode, een kans dat sommige lokale variabelen die vóór de aanroep van super() of this() zijn gemaakt, daarna nodig zijn.

In dergelijke gevallen heeft u de volgende mogelijkheden:

  1. Gebruik het patroon gepresenteerd op dit antwoord, waarmee u de beperking kunt omzeilen.
  2. Wacht tot het Java-team pre-super() en pre-this()-code toestaat. Dit kan worden gedaan door een beperking op te leggen waar super() of this() mag voorkomen in een constructor. Zelfs de huidige compiler is in staat om goede en slechte (of mogelijk slechte) gevallen te onderscheiden met een mate die voldoende is om veilig statische code-toevoeging aan het begin van een constructor toe te staan. Neem inderdaad aan dat super() en this() this-referentie retourneren en dat uw constructor op zijn beurt
return this;

aan het einde. Evenals de compiler verwerpt de 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;
}

met de fout “variabele x is mogelijk niet geïnitialiseerd”, zou het dit kunnen doen op this variabele, en zijn controles hierop uitvoeren net als bij elke andere lokale variabele. Het enige verschil is dat this niet kan worden toegewezen op een andere manier dan super() of this() aanroep (en, zoals gewoonlijk, als er is een dergelijke aanroep bij een constructor niet, super() wordt impliciet ingevoegd door de compiler in het begin) en wordt mogelijk niet twee keer toegewezen. In geval van twijfel (zoals in de eerste get(), waar x eigenlijk altijd is toegewezen), kan de compiler een fout retourneren. Dat zou beter zijn dan gewoon een fout te retourneren op een constructor waar iets anders is dan een opmerking vóór super() of this().


Antwoord 12

U kunt anonieme initialisatieblokken gebruiken om velden in het kind te initialiseren voordat de constructor wordt aangeroepen. Dit voorbeeld demonstreert:

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

Dit zal het volgende opleveren:

In ouder
In initialisatie
Bij kind


Antwoord 13

Het is logisch dat constructeurs hun uitvoering voltooien in volgorde van:
afleiding. Omdat een superklasse geen kennis heeft van een subklasse,
initialisatie die het moet uitvoeren staat los van en mogelijk
voorwaarde voor elke initialisatie die door de subklasse wordt uitgevoerd.
Daarom moet het eerst de uitvoering voltooien.

Een eenvoudige demonstratie:

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

De uitvoer van dit programma is:

Inside A's constructor
Inside B's constructor
Inside C's constructor

Antwoord 14

Ik weet dat ik een beetje laat op het feest ben, maar ik heb deze truc een paar keer gebruikt (en ik weet dat het een beetje ongebruikelijk is):

Ik maak een generieke interface InfoRunnable<T> met één methode:

public T run(Object... args);

En als ik iets moet doen voordat ik het aan de constructeur doorgeef, doe ik gewoon dit:

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

Antwoord 15

Eigenlijk is super() het eerste statement van een constructor om er zeker van te zijn dat de superklasse volledig gevormd is voordat de subklasse wordt geconstrueerd. Zelfs als je super() niet in je eerste statement hebt, zal de compiler het voor je toevoegen!


Antwoord 16

Dat komt omdat je constructor afhankelijk is van andere constructors. Om uw constructor correct te laten werken, is het noodzakelijk om andere constructor correct te laten werken, wat afhankelijk is. Daarom is het noodzakelijk om eerst afhankelijke constructors te controleren die door this() of super() in je constructor worden aangeroepen. Als andere constructors die door this() of super() worden aangeroepen, een probleem hebben, dus wat is het punt om andere instructies uit te voeren, want alles zal mislukken als de aangeroepen constructor faalt.


Antwoord 17

Tldr:

De andere antwoorden hebben het ‘waarom’ van de vraag aangepakt. Ik zorg voor een hack rond deze beperking:

Het basisidee is om de super-instructie te kapen met uw ingesloten instructies. Dit kunt u doen door uw uitspraken te vermommen als uitdrukkingen.

Tsdr:

Overweeg dat we Statement1() tot Statement9() willen doen voordat we super() aanroepen:

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

De compiler zal onze code natuurlijk afwijzen. Dus in plaats daarvan kunnen we dit doen:

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

De enige beperking is dat de ouderklasse een constructor moet hebben die ten minste één argument opneemt zodat we onze instructie als een uitdrukking kunnen binnensluipen.

Hier is een uitgebreider voorbeeld:

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

Omgewerkt tot:

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

In feite hadden compilers dit proces voor ons kunnen automatiseren. Ze hadden er gewoon voor gekozen om dat niet te doen.


Antwoord 18

Voordat u een onderliggend object kunt maken, moet uw bovenliggende object worden gemaakt.
Zoals je weet als je een les als volgt schrijft:

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

het gaat naar de volgende (uitbreiden en super zijn gewoon verborgen):

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

Eerst maken we een Object en breiden dit object vervolgens uit naar MyClass. We kunnen geen MyClass maken vóór het Object.
De eenvoudige regel is dat de constructor van de ouder moet worden aangeroepen vóór de constructor van het kind.
Maar we weten dat klassen meer dan één constructor kunnen hebben. Met Java kunnen we een constructor kiezen die wordt aangeroepen (het is super() of super(yourArgs...)).
Dus, wanneer je super(yourArgs...) schrijft, herdefinieer je de constructor die zal worden aangeroepen om een ​​bovenliggend object te maken. U kunt geen andere methoden uitvoeren vóór super() omdat het object nog niet bestaat (maar na super() wordt er een object gemaakt en kunt u doe alles wat je wilt).

Dus waarom kunnen we dan this() niet uitvoeren na welke methode dan ook?
Zoals je weet is this() de constructor van de huidige klasse. We kunnen ook een ander aantal constructors in onze klasse hebben en ze noemen zoals this() of this(yourArgs...). Zoals ik al zei, heeft elke constructor een verborgen methode super(). Wanneer we onze aangepaste super(yourArgs...) schrijven, verwijderen we super() met super(yourArgs...). Ook wanneer we this() of this(yourArgs...) definiëren, verwijderen we ook onze super() in de huidige constructor omdat als super() waren met this() in dezelfde methode, het zou meer dan één bovenliggend object creëren.
Daarom gelden dezelfde regels voor this() methode. Het verzendt gewoon de creatie van het bovenliggende object naar een andere onderliggende constructor en die constructor roept de super()-constructor aan voor het maken van de ouder.
Dus de code zal er in feite zo uitzien:

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

Zoals anderen zeggen dat je code als volgt kunt uitvoeren:

this(a+b);

je kunt ook code als volgt uitvoeren:

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

Maar je kunt code op deze manier niet uitvoeren omdat je methode nog niet bestaat:

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

U bent ook verplicht om de super()-constructor in uw keten van this()-methoden te hebben. U kunt een object niet als volgt maken:

public MyClass{
        public MyClass(int a) {
                this(a, 5);
        }
        public MyClass(int a, int b) {
                this(a);
        }
}

Antwoord 19

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

Zie het voorbeeld als we de constructor C(int x) aanroepen, dan is de waarde van z afhankelijk van y als we C() niet aanroepen in de eerste regel, dan is dit het probleem voor z. z zou de juiste waarde niet kunnen krijgen.


Antwoord 20

De vraag waarom Java dit doet is al beantwoord, maar aangezien ik op deze vraag stuitte in de hoop een beter alternatief voor de oneliner te vinden, deel ik hierbij mijn oplossing:

public class SomethingComplicated extends SomethingComplicatedParent {
    private interface Lambda<T> {
        public T run();
    }
    public SomethingComplicated(Settings settings) {
        super(((Lambda<Settings>) () -> {
            // My modification code,
            settings.setting1 = settings.setting2;
            return settings;
        }).run());
    }
}

Het aanroepen van een statische functie zou beter moeten presteren, maar ik zou dit gebruiken als ik erop sta de code “in” de constructor te hebben, of als ik meerdere parameters moet wijzigen en het definiëren van veel statische methoden slecht vind voor de leesbaarheid.

>


Antwoord 21

Het belangrijkste doel van het toevoegen van super() in de subklasse-constructors is dat de belangrijkste taak van de compiler is om een ​​directe of indirecte verbinding te maken van alle klassen met de klasse Object. Daarom controleert de compiler of we op voorwaarde dat de super (geparametriseerde) compiler geen enkele verantwoordelijkheid neemt.
zodat alle instantieleden worden geïnitialiseerd van Object naar de subklassen.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Other episodes