web-dev-qa-db-ja.com

なぜthis()とsuper()がコンストラクタの最初のステートメントにならならなければならないのですか?

Javaでは、コンストラクタ内でthis()またはsuper()を呼び出す場合は、それを最初の文にする必要があります。どうして?

例えば:

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

Sunのコンパイラは、「superの呼び出しはコンストラクタの最初のステートメントでなければならない」と言っています。 Eclipseのコンパイラは、「コンストラクタ呼び出しはコンストラクタの最初の文でなければならない」と言っています。

しかし、コードを少し変更することでこれを回避できます。

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

これは別の例です。

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

だから、それはsuperの呼び出しの前にロジックを実行するのを止めないのではない。 1つの式に収まらないのは、ロジックの実行を妨げるだけです。

this()を呼び出すための同様の規則があります。コンパイラは「これを呼び出すことはコンストラクタの最初のステートメントでなければならない」と言います。

コンパイラにこれらの制限があるのはなぜですか?コンパイラにこの制限がない場合に悪いことが起こるコード例を教えてください。

555
Joe Daley

親クラス 'constructorは、サブクラス' constructorの前に呼び出す必要があります。これにより、コンストラクタ内の親クラスのメソッドを呼び出した場合、その親クラスはすでに正しく設定されていることが保証されます。

あなたがやろうとしていること、スーパーコンストラクタに引数を渡すことは完全に合法です、あなたがしているようにインラインでそれらの引数を構築するか、コンストラクタにそれらを渡してsuperに渡す必要があります。

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

コンパイラがこれを強制しなかった場合、これを実行できます。

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

parentクラスにデフォルトのコンストラクタがある場合、superへの呼び出しはcompilerによって自動的に挿入されます。 JavaのすべてのクラスはObjectを継承しているので、オブジェクトコンストラクタはなんらかの方法で呼び出され、最初に実行される必要があります。コンパイラによるsuper()の自動挿入はこれを可能にします。 superを最初に出現させることで、コンストラクタ本体が正しい順序で実行されることを強制します。Object - > Parent - > Child - > ChildOfChild - > SoOnSoForth

174
anio

私はコンストラクタと静的メソッドを連鎖させることによってこれを回避する方法を見つけました。私がやりたかったことはこんな感じでした。

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

そのため、基本的にはコンストラクタパラメータに基づいてオブジェクトを構築し、そのオブジェクトをメンバに格納して、そのオブジェクトに対するメソッドの結果をsuperのコンストラクタに渡します。クラスの本質は不変であるということなので、メンバーをファイナルにすることもかなり重要です。実際には、Barの構築には実際にはいくつかの中間オブジェクトが必要なので、実際の使用例ではワンライナーにすることはできません。

私はそれを次のように機能させることにしました:

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

合法的なコードで、スーパーコンストラクタを呼び出す前に複数のステートメントを実行するというタスクを実行します。

92
pendor

JLSがそう言っているからです。 互換性のある方法でJLSを変更できますか?そうです。

しかし、それは言語仕様を複雑にするでしょう。それはすでに十分に複雑すぎる以上です。それを行うことはそれほど有用なことではないでしょうし、それを回避する方法があります(静的メソッドまたはラムダ式this(fn())の結果で別のコンストラクタを呼び出す - メソッドは他のコンストラクタの前に呼び出され、したがってスーパーコンストラクタも呼び出されます)。そのため、変更を行う際の電力対重量比は好ましくありません。

この規則だけでは、スーパークラスが構築を完了する前にフィールドの使用を妨げることはありません。

これらの違法な例を考えてください。

super(this.x = 5);

super(this.fn());

super(fn());

super(x);

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

この例は正当ですが「間違っています」。

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

上記の例で、MyDerived.fnMyDerivedコンストラクタからの引数を必要とする場合、それらはThreadLocalでスルースルーされる必要があります。 ;((

ちなみに、Java 1.4以降、外側のthisを含む合成フィールドは、内部クラスのスーパーコンストラクタが呼び出される前に割り当てられます。これにより、以前のバージョンをターゲットにするようにコンパイルされたコードで、固有のNullPointerExceptionイベントが発生していました。

また、安全でない出版物がある場合は、予防措置が講じられていない限り、他のスレッドによって構成が並べ替えられていると見なすことができます。

2018年3月に編集:メッセージの中に 記録:作成と検証 この制限を取り除くことをオラクルは推奨しています(がC#とは異なり、thisはコンストラクタチェーニングの前は確実に未割り当て(DU)になります。

歴史的に、this()またはsuper()はコンストラクタの最初になければなりません。この制限は一般的ではなく、恣意的なものとして認識されていました。 invokespecialの検証など、この制限の原因となっていた微妙な理由がいくつかありました。長年にわたり、我々はVMレベルで、レコードだけではなくすべてのコンストラクタに対してこの制限を解除することを検討することが現実的になるところまで対処してきました。

40

(a)部分的に構築されたオブジェクトの使用を許可されないようにすること、および(b)親クラスのコンストラクターに "新鮮な"構造を強制することを妨げることを防ぐためです(Java仕様に精通している人)。 "オブジェクト。

「悪い」ことのいくつかの例は次のようになります。

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

これが継承哲学だからこそ。そしてJava言語仕様によると、これはコンストラクタの本体がどのように定義されるかです:

ConstructorBody:{ExplicitConstructorInvocationopt BlockStatementsopt }

コンストラクタ本体の最初のステートメントは、

  • (キーワード "this"を使用して)同じクラスの別のコンストラクタを明示的に呼び出すまたは
  • 直接のスーパークラスの明示的な呼び出し(キーワード "super"を使用)

コンストラクタ本体が明示的なコンストラクタ呼び出しで始まっておらず、宣言されているコンストラクタが基底クラスObjectの一部ではない場合、コンストラクタ本体は暗黙のうちにスーパークラスのコンストラクタ呼び出し "super();"で始まります。引数を取らない直接スーパークラス。それから、Objectのコンストラクタまでさかのぼって呼ばれるコンストラクタの連鎖全体があります。 「JavaプラットフォームのすべてのクラスはObjectの子孫です」。これを "コンストラクタチェーン"と呼びます。

今これはなぜですか?
そして、JavaがこのようにConstructorBodyを定義した理由は、 階層を維持する オブジェクトの継承の定義を覚えておいてください。それはクラスを拡張しています。そうは言っても、存在しないものを拡張することはできません。ベース(スーパークラス)を最初に作成する必要があります。次にそれを派生させることができます(サブクラス)。だからこそ彼らは彼らを親クラスと子クラスと呼んだ。あなたは親なしで子供を持つことはできません。

技術レベルでは、サブクラスはその親からすべてのメンバー(フィールド、メソッド、ネストクラス)を継承します。そして、コンストラクタはメンバーではないので(それらはオブジェクトに属していません。それらはオブジェクトを作成する責任があります)、したがってそれらはサブクラスによって継承されませんが、それらは呼び出すことができます。そして、オブジェクト作成時の以来、1つのコンストラクタだけが実行されます。では、サブクラスオブジェクトを作成するときに、どのようにしてスーパークラスの作成を保証するのですかしたがって、「コンストラクタチェーン」の概念。そのため、現在のコンストラクタ内から他のコンストラクタ(つまりスーパー)を呼び出すことができます。そしてJavaは、階層を維持し、それを保証するために、この呼び出しがサブクラスのコンストラクターの最初の行であることを要求しました。あなたが明示的に親オブジェクトFIRSTを明示的に作成しない場合(あなたがそれを忘れた場合のように)、彼らはあなたのためにそれを暗黙のうちに行うと仮定します。

このチェックはコンパイル中に行われます。しかし、実行時に何が起こるのか、どのような実行時エラーが発生するのかわからない、IF Javaがサブクラスのコンストラクターの途中から明示的に基本コンストラクターを実行しようとしてもコンパイルエラーをスローしない体ではなく、最初の行から...

12
Randa Sbeity

あなたはなぜ尋ねたのですが、他の答えであるimoは、なぜあなたのスーパーのコンストラクタを呼んでも大丈夫なのかとは言わないでください。その理由は、あなたが本当にコンストラクタを呼ぶというわけではないからです。 C++では、同等の構文は次のとおりです。

MySubClass: MyClass {

public:

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

};

このようにイニシャライザ句を単独で見た場合、開き括弧の前に、それが特殊であることがわかります。他のコンストラクタが実行される前、そして実際にはメンバ変数が初期化される前に実行されます。 Javaでもそれほど違いはありません。サブクラスのメンバーが初期化される前に、コンストラクターが実際に開始される前にいくつかのコード(他のコンストラクター)を実行する方法があります。そしてその方法は一番最初の行に "call"(例えばsuper)を入れることです。 (ある意味では、superthisは最初の開き括弧の前のようなものです。たとえ後に入力したとしても、すべてが完全に構築されるようになる前に実行されるためです)。 (int c = a + b;のように)コンパイラに「ああ、そう、他のコンストラクタはありません。それですべてを初期化できます」とコンパイラに言わせます。それで、それは走り去ってあなたのスーパークラスとあなたのメンバーを初期化し、そして何もせずに開き括弧の後にコードの実行を開始します。

数行後に「このオブジェクトを構築するときは、そうです。基本クラスのコンストラクタに渡してほしいパラメータがあります」というコードに遭遇したとしても、それは遅すぎます。任意の意味をなすそのため、コンパイラエラーが発生します。

9
Kate Gregory

だから、それはあなたがsuperを呼び出す前にロジックを実行するのを止めているのではありません。 1つの式に収まらないのは、ロジックの実行を妨げるだけです。

実際には、いくつかの必要条件でロジックを実行できます。コードを静的関数にラップしてsuperステートメントで呼び出すだけで済みます。

あなたの例を使用して:

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;
    }
}
5
Tip-Sy

私は完全に同意する、制限が強すぎる。 (Tom Hawtin - tacklineが提案したように)静的ヘルパーメソッドを使用すること、またはすべての "pre-super()の計算"をパラメータ内の単一の式に代入することは必ずしも可能ではありません。

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

Carson Myersが示唆しているように、「オブジェクトはまだ構築されていません」という例外を使用すると効果的ですが、各オブジェクト構築中にこれをチェックすると実行が遅くなります。たとえ言語仕様が複雑になっても、(結果としてifステートメントを禁止し、パラメーター内で?演算子を許可するのではなく)より優れた区別をするJavaコンパイラーをお勧めします。

4
DaveFar

コンパイラにこの制限がない場合に悪いことが起こるコード例を教えてください。

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

構築中の例外はほとんどの場合、構築中のオブジェクトを正しく初期化できなかったことを示します。現在は不良状態にあり、使用不可能であり、ガベージコレクションを行う必要があります。ただし、サブクラスのコンストラクターは、そのスーパークラスの1つで発生した例外を無視し、部分的に初期化されたオブジェクトを返すことができます。上記の例で、new Bad()に与えられた引数が0か100より大きい場合、essential1essential2も正しく初期化されません。

あなたは例外を無視することは常に悪い考えであると言うかもしれません。さて、これはもう一つの例です。

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

おかしいですね。この例では、いくつのオブジェクトを作成していますか? 1?二?それとも何もない...

コンストラクタの途中でsuper()またはthis()を呼び出せるようにすると、Pandoraの箱詰めなコンストラクタが開きます。


一方、super()またはthis()を呼び出す前に、静的部分を含める必要が頻繁にあることを理解しています。これはthis参照に依存していないコード(実際には既にコンストラクタの先頭に存在していますが、super()またはthis()が戻るまで順番に使用することはできません)であり、そのような呼び出しを行うために必要なコードです。さらに、他のメソッドと同様に、super()またはthis()の呼び出し前に作成されたローカル変数が必要になる可能性があります。

そのような場合には、次のような機会があります。

  1. この答え に示されているパターンを使用してください。これにより制限を回避できます。
  2. Javaチームがpre -super()およびpre -this()コードを許可するのを待ちます。コンストラクタのどこでsuper()またはthis()が発生するかについて制限を課すことによってそれを行うことができます。実際、今日のコンパイラでも、コンストラクタの最初に静的コードの追加を安全に許可するのに十分な程度で、良い場合と悪い(または潜在的に悪い)ケースを区別することができます。確かに、super()this()this参照を返し、そしてあなたのコンストラクタは
return this;

最後に。コンパイラはコードを拒否します

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

エラー「変数xは初期化されていない可能性があります」では、他のローカル変数と同じようにチェックを行いながら、this変数に対して行うことができます。唯一の違いは、thissuper()またはthis()呼び出し以外の方法で割り当てることはできず(通常、コンストラクターでそのような呼び出しがない場合は、最初からsuper()が暗黙的にコンパイラーによって挿入される)、2回割り当てられないことです。疑問がある場合(最初のget()のように、実際には常にxが代入される)、コンパイラはエラーを返す可能性があります。 super()this()の前にコメント以外のものがあるコンストラクタで単にエラーを返すよりも良いでしょう。

3
John McClane

私は、Javaコードを処理するツールを書く人々の生活を楽にするため、そしてJavaコードを読む人々の生活を楽にするためにこれを行ったと思います。

super()またはthis()の呼び出しを回避することを許可している場合は、チェックすべきバリエーションがもっとあります。たとえば、super()またはthis()呼び出しを条件付きif()に移動する場合、暗黙のsuper()elseに挿入するのに十分なほど賢くなければなりません。 super()を2回呼び出す場合、またはsuper()this()を一緒に使用する場合は、エラーを報告する方法を知る必要があるかもしれません。 super()またはthis()が呼び出されるまで受信側でメソッド呼び出しを許可しないようにし、それがいつ複雑になるかを把握する必要があるかもしれません。

この余分な作業をすべての人にやらせることは、おそらく利益よりも大きなコストのように思えました。

3

無名コンストラクタを呼び出す前に、無名イニシャライザブロックを使用して子のフィールドを初期化できます。この例は次のことを示します。

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

これは出力されます:

親に
イニシャライザで
こどもの中

2

私はぐらつきを見つけました。

これはコンパイルされません:

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

これは動作します:

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

コンストラクタが派生順に実行を完了することは理にかなっています。スーパークラスはサブクラスの知識がないため、実行する必要がある初期化は、サブクラスによって実行される初期化とは別のものであり、場合によってはその前提条件でもあります。したがって、最初に実行を完了する必要があります。

簡単なデモンストレーション:

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

このプログラムからの出力は次のとおりです。

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

実際には、super()はコンストラクタの最初のステートメントです。スーパークラスがサブクラスの構築前に完全形式であることを確認するためです。最初のステートメントにsuper()がない場合でも、コンパイラーはそれを追加します。

それはあなたのコンストラクタが他のコンストラクタに依存しているからです。あなたのコンストラクタにとって正しく機能すること他のコンストラクタにとって必要なことは正しく依存していることによって正しく機能すること。そのため、コンストラクタ内でthis()またはsuper()によって呼び出された依存コンストラクタを最初にチェックする必要があります。 this()またはsuper()によって呼び出された他のコンストラクタに問題がある場合は、呼び出されたコンストラクタが失敗するとすべてが失敗するため、他のステートメントを実行する必要があります。

1
Farrukh Ahmed

私はパーティーに少し遅れていることを知っています、しかし私は数回このトリックを使いました(そしてそれは少し変わっていることを私は知っています):

私は1つのメソッドで汎用インターフェースInfoRunnable<T>を作成します。

public T run(Object... args);

そしてそれをコンストラクタに渡す前に何かする必要があるなら、私はこれをするだけです:

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

Tldr:

他の答えは質問の「なぜ」に取り組んだ。この制限を回避するために、ハックを提供します。

基本的な考え方は、埋め込みステートメントを使ってsuperステートメントをハイジャックすることです。これは、ステートメントを のように見せかけることで実行できます。

Tsdr:

Statement1()を呼び出す前に、Statement9()から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
    }
}

コンパイラはもちろん私たちのコードを拒否します。その代わりに、これを行うことができます。

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

唯一の制限は、親クラスが少なくとも1つの引数を取り込むコンストラクターを持っている必要があるということです。これにより、ステートメント内で式としてこっそり入ることができます。

これはもっと複雑な例です:

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

に修正されました:

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

実際、コンパイラはこのプロセスを自動化している可能性があります。彼らはしないことを選んだだけでした。

0
Pacerier

子オブジェクトを構築する前に、親オブジェクトを作成する必要があります。あなたがこのようなクラスを書くときにあなたが知っているように:

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

次の話になります(extendとsuperは隠されているだけです):

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

まずObjectを作成し、次にこのオブジェクトをMyClassに拡張します。 MyClassObjectの前に作成することはできません。簡単な規則は、親のコンストラクタは子コンストラクタの前に呼び出す必要があるということです。しかし、クラスは複数のコンストラクタを持つことができます。 Javaでは、呼び出されるコンストラクタを選択することができます(それはsuper()またはsuper(yourArgs...)のどちらかになります)。そのため、super(yourArgs...)を書くときには、親オブジェクトを作成するために呼び出されるコンストラクタを再定義します。オブジェクトがまだ存在しないため、super()の前に他のメソッドを実行することはできません(ただし、super()の後にオブジェクトが作成され、必要なことは何でもできます)。

それでは、どうしてメソッドの後でthis()を実行できないのでしょうか。ご存じのとおり、this()は現在のクラスのコンストラクタです。また、クラス内に異なる数のコンストラクタを配置して、それらをthis()this(yourArgs...)のように呼び出すこともできます。私が言ったように、すべてのコンストラクタはsuper()メソッドを隠しています。カスタムのsuper(yourArgs...)を書くとき、super()super(yourArgs...)で削除します。また、this()またはthis(yourArgs...)を定義するとき、現在のコンストラクタでsuper()も削除します。これは、super()が同じメソッド内でthis()と一緒に使用された場合、複数の親オブジェクトが作成されるためです。これがthis()メソッドにも同じ規則が課されている理由です。親オブジェクトの作成を別の子コンストラクターに再送信するだけで、そのコンストラクターは親作成のためにsuper()コンストラクターを呼び出します。そのため、コードは実際には次のようになります。

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

他の人が言うように、あなたはこのようなコードを実行することができます:

this(a+b);

また、あなたはこのようなコードを実行することができます:

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

しかし、あなたのメソッドはまだ存在しないので、このようなコードを実行することはできません。

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

super()メソッドのチェーンの中にthis()コンストラクタを持つことも義務付けられています。このようなオブジェクトを作成することはできません。

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

最初の行でC(int x)を呼び出さない場合、コンストラクタC()を呼び出しているときにzの値がyに依存している場合は例を参照してください。それがzの問題になります。 zは正しい値を取得できません。

0
PANKAJ KUMAR