2009-06-08 23 views
2

次の問題を解決する最善の方法について意見を述べることができますか?私はinitブロックを試して、コンストラクタのフィールドを初期化しましたが、オーバーライドされたメソッドがそれをクリアしようとする前に呼び出されることもありません。オーバーライドメソッドを使用したNullPointerException

class TestRunner { 
     public static void main(String[] args) { 
      ftw ftw = new ftw(); 
      ftw.dontPanic(); 
     } 
    } 

    class wtf { 

     wtf(){ 
      this.dontPanic(); 
     } 

     void dontPanic() { 
      System.out.println("super don't panic"); 
     } 
    } 

    class ftw extends wtf { 
     final HashSet variableOfDoom = new HashSet(); 

     ftw(){ 
      super(); 
     } 

     void dontPanic() { 
      super.dontPanic(); 
      System.out.println("sub don't panic"); 
      variableOfDoom.clear(); //This line prints a null pointer exception, because the field hasn't been intialized yet. 
     } 
    } 

答えて

7

これは、非finalメソッドをコンストラクタ内から呼び出さないことをお勧めしている理由です。 privateメソッドもオーバーライドできないので安全です。私は例のためにあなたが単純化されたと仮定してい

  • を、しかしコール:

    あなたには、いくつかの理由で、ここに役立つかもしれないいくつかの他の観測があり、そのサウンドアドバイスに従うことができないと仮定すると、 .clear()に設定すると、この例では実際には何も達成されません。あなたは単にそれを削除することができるかもしれません。

  • 合計で実際のプログラムを見ずに言うのは難しいですが、.clear()を呼び出す代わりに、変数に新しいHashSetを割り当てることでスケートすることができます。ここで
+0

これは間違いなく、クリアは例外をスローするための単なる例でした。 – danieljimenez

+0

私はこれが推奨される習慣であることに気付かなかったので、私はこの回答を受け入れています。私はIntelliJの検査を可能にして、私たちがこれをやり遂げるのを妨げています。ありがとうハンク! – danieljimenez

+0

コンストラクタの外側(この場合はdontPanic()メソッド)のどこにでも、最終変数(この場合variableOfDoom)に新しいHashSetを割り当てることはできません。それはあなたの第2のポイントが何を指しているのでしょうか? – neesh

7

は問題だ:あなたの実行の流れは次のようになります。

main 
->ftw() 
    ->wtf() 
    ->wtf.dontPanic() 

しかし、メソッド呼び出しは、Javaで実行時に決定されているので、ftw.dontPanic()は本当にと呼ばれています方法です。 variableOfDoomは、コンストラクターが完了するまで設定されませんが、例外のためコンストラクターは決して完了しません。

解決策は、コンストラクタ内で作業することではありません(少なくともprivateの非finalメソッドではない)。

+0

+1は30秒で私より速くなります。 –

+0

はい私は問題とexecフローを認識していますが、これを回避するためのベストプラクティスに関する提案を探しています。 – danieljimenez

+0

これは、 "コンストラクタ内でプライベート以外の非最終メソッドを呼び出さない"ビットとなります。 –

2

スーパークラスの初期化は、完全に初期化される子クラスに依存できません。

もしftwがwtfを超えていなければ、フィールド定義で指定された初期化はコンストラクタが呼び出される前に行われると仮定するのが大丈夫です。しかし、ftwはwtfを拡張するので、wtwはftwで初期化が行われる前に完全に初期化されなければなりません。 wtf初期化の一部は、サブクラス内のvariableOfDoomが初期化されているため、NULLポインタ例外が発生しています。

これは、dontPanicへの呼び出しをコンストラクタの外側で分割するための唯一の方法です。

0

コンストラクタ内で非最終メソッドを呼び出さないでください - すべてのインスタンス変数が初期化されているわけではありません - コードは古典的な例です。

なしあなたが本当に、達成しようとし、「問題を解決する最良の方法」をお勧めする。その難しいが、ここでは、外出先だしているか知っている:

class TestRunner { 
    public static void main(String[] args) { 
      ftw ftw = new ftw(); 
      ftw.dontPanic(); 
    } 
} 

class wtf { 

    wtf(){ 
      wtfDontPanic(); 
    } 

    private final void wtfDontPanic() { 
      System.out.println("super don't panic"); 
    } 

    void dontPanic() { 
      wtfDontPanic(); 
    } 
} 

class ftw extends wtf { 
    final HashSet variableOfDoom = new HashSet(); 

    ftw(){ 
      super(); 
      ftwDontPanic(); 
    } 

    private final ftwDontPanic() { 
      System.out.println("sub don't panic"); 
      variableOfDoom.clear(); //This line prints a null pointer exception, because the field hasn't been intialized yet. 
    } 

    private void dontPanic() { 
      super.DontPanic(); 
      ftwDontPanic(); 
    } 
} 
1

あなたはプライベートメソッドが防止されていないことに注意かもしれませんオーバーライドを呼び出すことから。その場合でも、コンストラクタからオーバーライドされたものを呼び出す際には注意が必要です。他の言語では、メソッドが呼び出されたときにオブジェクトが完全に構築されない可能性があるため、コンストラクターから仮想メソッドを呼び出さないようにすることがアドバイスです。

考慮すべき代替案は2段階構成です。 http://www.artima.com/forums/flat.jsp?forum=106&thread=106524

「create-set-call」という.NET Framework Design Guidelinesブックには、関連するイディオムがあります。オブジェクトを作成し、いくつかのプロパティを設定し、いくつかのメソッドを呼び出します。私は、このような使い方が、単純な構築コール(つまり、単に作成と使用)よりも発見されにくいと批判されることがあることを見てきました。

0

迅速&汚いソリューション(非常に良いではない、私は知っている)

... 
System.out.println("sub don't panic"); 
// must test if object fully initialized, method called in constructor 
if (variableOfDoom != null) { 
    variableOfDoom.clear(); 
} 
+0

そしてvariableOfDoomはまだ最終的なことができます。 –

+0

なぜdownvote(6ヶ月後)ですか? –

1

まず、それをしません。あなたは絶対にそれをしなければならない場合

第二に、これを行う:あなたはvariableOfDoomを持つことはできません

class wtf { 

    wtf(){ 
      this.dontPanic(); 
    } 

    void dontPanic() { 
      System.out.println("super don't panic"); 
    } 
} 

class ftw extends wtf { 
    HashSet _variableOfDoom; // underscore to remind you not to access it directly 

    ftw(){ 
      super(); 
    } 

    private HashSet getVariableOfDoom() 
    { 
     if (_variableOfDoom == null) _variableOfDoom = new HashSet(); 
     return _variableOfDoom; 
    } 

    void dontPanic() { 
      super.dontPanic(); 
      System.out.println("sub don't panic"); 
      getVariableOfDoom().clear(); // FOR GREAT JUSTICE! 
    } 
} 

注意が最終的なもの。

+0

HashSetを作成してクリアできる理由がわかりません。私はnull値をチェックし、初期化されていなければそれをクリアする必要があると仮定します。 –