725 Stimmen

Warum müssen this() und super() die erste Anweisung in einem Konstruktor sein?

Java schreibt vor, dass der Aufruf von this() oder super() in einem Konstruktor die erste Anweisung sein muss. Warum?

Zum Beispiel:

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

Der Sun-Compiler sagt: "Der Aufruf von super muss die erste Anweisung im Konstruktor sein". Der Eclipse-Compiler sagt: "Constructor call must be the first statement in a constructor".

Sie können dies jedoch umgehen, indem Sie den Code ein wenig umgestalten:

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

Hier ein weiteres Beispiel:

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

Es ist also Sie nicht daran hindern, die Logik auszuführen vor dem Anruf beim Hausmeister. Es hält Sie nur davon ab, Logik auszuführen, die Sie nicht in einen einzigen Ausdruck packen können.

Es gibt ähnliche Regeln für den Aufruf this() . Der Compiler sagt: "Der Aufruf von this muss die erste Anweisung im Konstruktor sein".

Warum hat der Compiler diese Einschränkungen? Können Sie ein Codebeispiel nennen, bei dem etwas Schlimmes passieren würde, wenn der Compiler diese Einschränkung nicht hätte?

9 Stimmen

Eine gute Frage. Ich habe ein ähnliches Projekt gestartet valjok.blogspot.com/2012/09/ und programmers.exchange, wo ich zeige, dass es Fälle gibt, in denen Unterfelder vor dem super() initialisiert werden müssen. Die Funktion erhöht also die Komplexität, wobei nicht klar ist, ob die positiven Auswirkungen in Bezug auf die "Codesicherheit" die negativen überwiegen. Ja, es gibt negative Konsequenzen, wenn super immer zuerst kommt. Überraschenderweise hat dies niemand erwähnt. Ich denke, dass dies eine konzeptionelle Angelegenheit ist und in programmers.exchange gefragt werden muss.

60 Stimmen

Das Schlimmste daran ist, dass dies eine reine Java-Beschränkung ist. Auf der Bytecode-Ebene gibt es keine solche Einschränkung.

2 Stimmen

Nun, es wäre unmöglich, diese Einschränkung auf der Bytecode-Ebene zu haben - alle Beispiele in diesem Beitrag würden eine solche Einschränkung verletzen, auch die, die die gesamte Logik in einen einzigen Ausdruck packen.

1voto

Eigentlich, super() ist die erste Anweisung eines Konstruktors, weil sichergestellt werden soll, dass die Oberklasse vollständig ausgebildet ist, bevor die Unterklasse konstruiert wird. Auch wenn Sie nicht über super() in Ihrer ersten Anweisung, der Compiler fügt sie für Sie ein!

1voto

Raslanove Punkte 578

Die Frage, warum Java dies tut, wurde bereits beantwortet, aber da ich in der Hoffnung, eine bessere Alternative zum Einzeiler zu finden, über diese Frage gestolpert bin, werde ich hiermit meine Lösung mitteilen:

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

Der Aufruf einer statischen Funktion sollte besser funktionieren, aber ich würde dies verwenden, wenn ich darauf bestehe, den Code "innerhalb" des Konstruktors zu haben, oder wenn ich mehrere Parameter ändern muss und die Definition vieler statischer Methoden schlecht für die Lesbarkeit finde.

0voto

Oleksandr Punkte 3099

Bevor Sie ein untergeordnetes Objekt erstellen können, muss das übergeordnete Objekt erstellt werden. Wie Sie wissen, wenn Sie eine Klasse wie diese schreiben:

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

wendet es sich dem nächsten zu (extend und super sind einfach ausgeblendet):

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

Zunächst erstellen wir eine Object und erweitern Sie dann dieses Objekt zu MyClass . Wir können nicht erstellen MyClass vor der Object . Die einfache Regel lautet, dass der Konstruktor des Elternteils vor dem Konstruktor des Kindes aufgerufen werden muss. Aber wir wissen, dass Klassen mehr als einen Konstruktor haben können. Java erlaubt es uns, einen Konstruktor zu wählen, der aufgerufen wird (entweder ist es der super() o super(yourArgs...) ). Wenn Sie also schreiben super(yourArgs...) definieren Sie den Konstruktor neu, der aufgerufen wird, um ein übergeordnetes Objekt zu erstellen. Sie können keine anderen Methoden ausführen, bevor super() weil das Objekt noch nicht existiert (aber nach super() wird ein Objekt erstellt, mit dem Sie alles machen können, was Sie wollen).

Warum können wir dann nicht ausführen this() nach irgendeiner Methode? Wie Sie wissen this() ist der Konstruktor der aktuellen Klasse. Wir können auch eine unterschiedliche Anzahl von Konstruktoren in unserer Klasse haben und sie wie folgt aufrufen this() o this(yourArgs...) . Wie ich schon sagte, hat jeder Konstruktor eine versteckte Methode super() . Wenn wir unseren Auftrag schreiben super(yourArgs...) wir entfernen super() con super(yourArgs...) . Auch wenn wir definieren this() o this(yourArgs...) entfernen wir auch unsere super() im aktuellen Konstruktor, denn wenn super() waren mit this() in der gleichen Methode, würde es mehr als ein Elternobjekt erzeugen. Deshalb gelten die gleichen Regeln wie für this() Methode. Sie überträgt lediglich die Erstellung des Elternobjekts an einen anderen Kindkonstruktor und dieser Konstruktor ruft super() Konstruktor für die Erstellung der Eltern. Also, der Code wird in der Tat so aussehen:

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

Wie andere sagen, können Sie Code wie diesen ausführen:

this(a+b);

Sie können auch Code wie diesen ausführen:

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

Aber Sie können diesen Code nicht ausführen, weil Ihre Methode noch nicht existiert:

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

Außerdem sind Sie verpflichtet, eine super() Konstruktor in Ihrer Kette von this() Methoden. So kann man kein Objekt erstellen:

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

0voto

PANKAJ KUMAR Punkte 21
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);
    }
}

Siehe das Beispiel, wenn wir den Konstruktor aufrufen C(int x) dann ist der Wert von z abhängig von y, wenn wir nicht C() in der ersten Zeile, dann gibt es ein Problem für z. z kann dann nicht den richtigen Wert bekommen.

0voto

Pacerier Punkte 80774

Tldr:

Die anderen Antworten haben sich mit dem "Warum" der Frage befasst. Ich werde eine hacken. um diese Einschränkung zu umgehen:

Der Grundgedanke ist, dass entführen. le site super Anweisung mit Ihren eingebetteten Anweisungen. Dies kann durch die Verkleidung Ihrer Anweisungen als Ausdrücke .

Tsdr:

Wir wollen Folgendes tun Statement1() a Statement9() bevor wir aufrufen 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
    }
}

Der Compiler wird unseren Code natürlich ablehnen. Also können wir stattdessen dies tun:

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

Die einzige Einschränkung ist, dass die Die übergeordnete Klasse muss einen Konstruktor haben, der mindestens ein Argument entgegennimmt so dass wir unsere Aussage als Ausdruck einfügen können.

Hier ist ein ausführlicheres Beispiel:

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

Umgearbeitet in:

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

Die Compiler hätten diesen Prozess für uns automatisieren können. Sie haben es nur nicht getan.

1 Stimmen

Im zweiten Codeblock, super(F(), _2, _3); sollte sein super(F(_1), _2, _3);

0 Stimmen

"Die übergeordnete Klasse muss einen Konstruktor haben, der mindestens ein Argument aufnimmt" ist nicht wahr - machen Sie einfach einen weiteren Konstruktor in Ihrer eigenen Klasse, der ein Argument aufnimmt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X