48

したがって、Javaでは、コンストラクターの最初の行はsuperの呼び出しである必要があります...暗黙的にsuper()を呼び出す場合でも、明示的に別のコンストラクターを呼び出す場合でも同じです。私が知りたいのは、なぜその周りにトライブロックを配置できないのですか?

私の特定のケースは、テスト用のモッククラスがある場合です。デフォルトのコンストラクターはありませんが、テストを読みやすくするためのコンストラクターが必要です。また、コンストラクターからスローされた例外をRuntimeExceptionにラップしたいと思います。

だから、私がやりたいのは事実上これです:

public class MyClassMock extends MyClass {
    public MyClassMock() {
        try {
            super(0);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // Mocked methods
}

しかし、Javaは、superが最初のステートメントではないと不満を漏らしています。

私の回避策:

public class MyClassMock extends MyClass {
    public static MyClassMock construct() {
        try {
            return new MyClassMock();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public MyClassMock() throws Exception {
        super(0);
    }

    // Mocked methods
}

これは最善の回避策ですか?なぜJavaは私に前者をさせないのですか?


「なぜ」についての私の最も良い推測は、Javaが、潜在的に矛盾した状態の構築されたオブジェクトを私に持たせたくないということです...しかし、モックを行う際には、私はそれを気にしません。私は上記を行うことができるはずです...または少なくとも私は上記が私の場合には安全であることを知っています...またはとにかくそうあるべきであるように見えます。

テストされたクラスから使用するメソッドをオーバーライドしているので、初期化されていない変数を使用しているリスクはありません。

4

7 に答える 7

18

残念ながら、コンパイラは理論上の原則に取り組むことができません。あなたの場合は安全だと知っていても、許可されていれば、すべての場合に安全でなければなりません。

言い換えれば、コンパイラはあなただけを止めているのではなく、それが安全でなく、特別な処理が必要であることを知らないすべての人を含めて、すべての人を止めています。これにはおそらく他の理由もあります。すべての言語には、それらに対処する方法を知っていれば、通常、安全でないことを行う方法があるからです。

C#.NETにも同様の規定があり、基本コンストラクターを呼び出すコンストラクターを宣言する唯一の方法は次のとおりです。

public ClassName(...) : base(...)

その際、基本コンストラクターはコンストラクターの本体の前に呼び出され、この順序を変更することはできません。

于 2008-08-07T21:32:34.367 に答える
10

SecurityManagerこれは、信頼できないコードから誰かが新しいオブジェクトを作成するのを防ぐために行われます。

public class Evil : SecurityManager {
  Evil()
  {
      try {
         super();
      } catch { Throwable t }
      {
      }
   }
}
于 2008-09-16T20:55:01.563 に答える
9

これは古い質問であることは知っていますが、私はそれが好きだったので、私自身の答えを出すことにしました. おそらく、これができない理由についての私の理解は、あなたの興味深い質問の議論と将来の読者に貢献するでしょう.

オブジェクト構築の失敗例から始めましょう。

次のようにクラス A を定義しましょう。

class A {
   private String a = "A";

   public A() throws Exception {
        throw new Exception();
   }
}

ここで、try...catchブロックにタイプ A のオブジェクトを作成したいとします。

A a = null;
try{
  a = new A();
}catch(Exception e) {
  //...
}
System.out.println(a);

明らかに、このコードの出力は次のようになりますnull

Java が の部分的に構築されたバージョンを返さないのはなぜAですか? 結局、コンストラクターが失敗する時点で、オブジェクトのnameフィールドは既に初期化されていますよね?

Aオブジェクトが正常に構築されなかったため、Java は の部分的に構築されたバージョンを返すことができません。オブジェクトは一貫性のない状態にあるため、Java によって破棄されます。変数 A は初期化されておらず、null のままです。

ご存知のように、新しいオブジェクトを完全に構築するには、そのすべてのスーパー クラスを最初に初期化する必要があります。スーパー クラスの 1 つが実行に失敗した場合、オブジェクトの最終的な状態はどうなるでしょうか? それを決定することは不可能です。

このより精巧な例を見てください

class A {
   private final int a;
   public A() throws Exception { 
      a = 10;
   }
}

class B extends A {
   private final int b;
   public B() throws Exception {
       methodThatThrowsException(); 
       b = 20;
   }
}

class C extends B {
   public C() throws Exception { super(); }
}

のコンストラクターCが呼び出されたときに、初期化中に例外が発生しBた場合、最終的なint変数の値はどうなるでしょうbか?

そのため、オブジェクト C は作成できず、偽物であり、ゴミであり、完全に初期化されていません。

私にとって、これはあなたのコードが違法である理由を説明しています。

于 2012-04-21T17:09:23.910 に答える
1

Javaが内部でどのように実装されているかはわかりませんが、スーパークラスのコンストラクターが例外をスローした場合、拡張するクラスのインスタンスはありません。たとえば、toString()またはメソッドはほとんどの場合継承されるため、これらを呼び出すことはできません。equals()

Javaは、1。スーパークラスのすべてのメソッドをオーバーライドし、2。super.XXX()句を使用しない場合、コンストラクターでのsuper()呼び出しの試行/キャッチを許可する場合がありますが、すべてが複雑すぎて自分。

于 2008-08-07T21:41:59.917 に答える
1

Java の内部構造を深く理解しているとは思えませんが、コンパイラが派生クラスをインスタンス化する必要がある場合、最初にベース (およびそのベース (...)) を作成する必要があることを理解しています。次に、サブクラスで作成された拡張機能を平手打ちします。

したがって、初期化されていない変数やそのようなものの危険さえありません。基本クラスのコンストラクターの前にサブクラスのコンストラクターで何かをしようとすると、基本的に、まだ存在しない基本オブジェクトのインスタンスを拡張するようにコンパイラーに要求しています。

編集:あなたの場合、MyClassは基本オブジェクトになり、MyClassMockはサブクラスです。

于 2008-08-07T21:43:37.463 に答える
-1

これを回避する 1 つの方法は、プライベートな静的関数を呼び出すことです。次に、try-catch を関数本体に配置できます。

public class Test  {
  public Test()  {
     this(Test.getObjectThatMightThrowException());
  }
  public Test(Object o)  {
     //...
  }
  private static final Object getObjectThatMightThrowException()  {
     try  {
        return  new ObjectThatMightThrowAnException();
     }  catch(RuntimeException rtx)  {
        throw  new RuntimeException("It threw an exception!!!", rtx);
     }
  }
}
于 2014-03-28T19:35:33.583 に答える