2013-03-10 10 views
11

Runnableインターフェイスを実装する抽象的なBaseクラスがあるとしましょう。Javaのコンストラクタから抽象メソッドを呼び出すことはできますか?

public abstract class Base implements Runnable { 

    protected int param; 

    public Base(final int param) { 
     System.out.println("Base constructor"); 
     this.param = param; 
     // I'm using this param here 
     new Thread(this).start(); 
     System.out.println("Derivative thread created with param " + param); 
    } 

    @Override 
    abstract public void run(); 
} 

ここでは、いくつかの派生クラスの1つです。

public class Derivative extends Base { 

    public Derivative(final int param) { 
     super(param); 
    } 

    @Override 
    public void run() { 
     System.out.println("Derivative is running with param " + param); 
    } 

    public static void main(String[] args) { 
     Derivative thread = new Derivative(1); 
    } 

} 

ポイントは、私は私の基本クラスがいくつかの一般的なものはなく、それを毎回コピーを行いたいということです。 1デリバティブ はPARAM 1

で実行されている。しかし、それはJavaで安全であるのparamで作成したデリバティブスレッドは、スレッドが呼び出しを開始する

基本コンストラクタ:実は は、出力が常に同じで、細かい動作していますコンストラクタの抽象メソッド?なぜなら、C++やC#では、ほとんどの場合、私が知る限り安全ではないからです。 ありがとうございました!

答えて

21

このコードは、実証することができます。これは、コンストラクタに持つ唯一の「安全な」メソッドが、プライベートなものと最終的なものであることを意味します。

一方、あなたのコードは、実際にはコンストラクタから抽象メソッドを呼び出すわけではありません。代わりに、初期化されていないオブジェクトを処理のために別のスレッドに渡します。これは悪いことです。開始するスレッドに優先順位が与えられ、Baseの初期化が完了する前に実行される可能性があります。オブジェクトが不整合な状態にある可能性があるため、コンストラクタの外にthisを渡す

+1

コンストラクタがコンストラクタから呼び出すことができ、そのようなコンテキストから呼び出すことが安全でなければならず、同様に他のメソッドしか呼び出せないと指定した抽象メソッドまたは仮想メソッド*を呼び出すことについては、安全*? – supercat

+1

@supercat:文書をどのくらい信頼しているか、他の人がそのような文書に従うことをどれほど信頼するか、またそのようなクラスやそのサブクラスを拡張して、適切に伝播したり覚えている人そのような警告を参照してください。それは私の意見ではかなりの信頼です。私は、自動化されたテストによって証明できるものを好んでおり、それはできません。 –

+2

十分に良い。私が念頭に置いていた最大の使用例は、派生クラスが、各サブクラスのすべてのインスタンスに対して同じでなければならない定数を返すメソッドをオーバーライドすることが予想され、コンストラクタやその他の場所で必要とされる場合、典型的な実装は 'int getWoozleForType(){return 23;}'です。そのようなものをコンストラクタに渡してインスタンスフィールドに格納するのはちょっと厄介なようですが、私はどちらかを招待しているようなアプローチは考えられません。 – supercat

3

コンストラクタから抽象メソッドを呼び出すことは非常に悪い習慣です。コンストラクタから呼び出されるメソッドは、オーバーライドを防ぐために、常にprivateまたはfinalである必要があります。

質問here

2

ない良いアイデアには、このリンクを参照してください、実行()が呼び出されたとき以来、デリバティブオブジェクトが初期化されていない可能性があります。 run()がDerivativeの状態に依存する場合、失敗する可能性があります。

単純なケースでは機能します。しかし、サブクラスには意味がありません。

abstract class Super { 
    Super() { 
     doSubStuff(); 
    } 
    abstract void doSubStuff(); 
} 

class Sub extends Super { 
    String s = "Hello world"; 

    void doSubStuff() { 
     System.out.println(s); 
    } 
} 

public static void main(String[] args) { 
    new Sub(); 
} 

実行すると、これはnullを印刷します:なぜあなたは、コンストラクタから決してコール抽象メソッド、または任意の他のオーバーライドメソッド、すべきであるあなたは、単に

public Base(final int param, Runnable action) { 

    new Thread(action).start(); 
1

は、「コンストラクタからthisを逃がす」と呼ばれ、いくつかの特に厄介なと奇妙なバグにつながることができます。

この例のように、thisが別のスレッドに渡された場合は特にそうです。 JVMはスレッド内のステートメントの順序を変更できるため、未定義の動作/状態が発生する可能性があります。

関連する問題