2017-11-06 5 views
0

この質問は少し先進的なので、やや複雑です。私は可能な限り明確になるよう努力して努力します。Java Genericsを使用してタイプ制限を適用する方法

タイトルのとおり、Java Genericsを使用して、トップレベル(メイン)のオブジェクトを構築するときにタイプ制限を適用したいと考えています。

私はJavaのジェネリックを実際に使用したことはありませんが、実装方法がわかりません。

オブジェクトを作成するときにタイプ制限を適用したいと思います。

ここでは、メソッドの初期化と呼び出しを行うNumberEngineオブジェクトを呼び出すトップレベルのmainメソッドがあります。 setExecuteBehavior()を呼び出すと、RunNumberEvaluation型のオブジェクト(RunStringEvaluationとともにExecutionBehaviorというインターフェイスを実装しています)を渡します。

名前が示すように、NumberEngineはNumberでのみ動作し、Stringでは動作しないため、setExecuteBehavior()にRunStringEvaluation型のオブジェクトを渡すのは不適切です。コンパイル時にこの動作を強制するにはどうすればよいですか?

public static void main(String[] args) { 

    NumberEngine numberEngine = new NumberEngine(); 
    numberEngine.init("/path/to/forms"); 
    numberEngine.getEngineVesion(); 

    numberEngine.setExecuteBehavior(new RunNumberEvaluation); 
    numberEngine.performExecution(); 


    // Here this should not compile, essentially throw me a compile error saying it can only accept 
    // an object of type RunNumberEvaluation, sincle NumberEngine can only run 
    // objects of type RunNumberEvaluation, etc... 
    numberEngine.setExecuteBehavior(new RunStringEvaluation()); 
    numberEngine.performExecution(); 

} 

だからここに私は基本的にNumberEngineのsetExecuteBehaviorは数字だけではなく文字列に関連するデータの処理などのそれにreleventある行動を受け入れるようにしたいと思います。また、StringEngineの場合も同様です。私はStringEngineがNumberではなくStringsにしか関係のないオブジェクトを受け入れるようにします。

これをJava genericsでどうすれば実現できますか?私は「何の実例として、以下の作業のコードが含まれている私は...このような何か...これは理にかなっている場合

NumberEngine<? extends Numbers> extends Engine 

さえわからない

を考えていた

通信しようとしています。

私はStringEngine、NumberEngineなどのような多くの拡張コンクリートクラスを持つ抽象クラスであるEngine型のオブジェクトを持っています。私はアルゴリズムの機能性を、そのインタフェースを実装するクラスとのインタフェースに切り離しました。

基本抽象クラス

public abstract class Engine { 
    ExecuteBehavior executeBehavior; 

    public void setExecuteBehavior(ExecuteBehavior executeBehavior) { 
     this.executeBehavior = executeBehavior; 
    } 
    public void performExecution() { 
     executeBehavior.execute(); 
    } 
    public abstract void init(String pathToResources); 
} 

コンクリート実装するクラス1

public class StringEngine extends Engine { 
    public StringEngine() { 
     executeBehavior = new RunNumberEvaluation(); 
    } 

    @Override 
    public void init(String pathToResources) { 
     System.out.println("Initializing StringEngine with resources "+pathToResources); 
     System.out.println("Successfully initialized StringEngine!"); 
    } 
} 

コンクリート実装するクラス2

public class NumberEngine extends Engine { 
    public NumberEngine() { 
     executeBehavior = new RunStringEvaluation(); 
    } 

    @Override 
    public void init(String pathToResources) { 
     System.out.println("Initializing NumberEngine with resources "+pathToResources); 
     System.out.println("Successfully initialized NumberEngine!"); 
    } 
} 

アルゴリズムインタフェース

public interface ExecuteBehavior { 
    void execute(); 
} 

アルゴリズム実装1

public class RunNumberEvaluation implements ExecuteBehavior { 
    @Override 
    public void execute() { 
     // some processing 
     System.out.println("Running numeric evaluation"); 
    } 
} 

アルゴリズム実装2

public class RunStringEvaluation implements ExecuteBehavior { 
    @Override 
    public void execute() { 
     // some processing 
     System.out.println("Running string evaluation"); 
    } 
} 

あなたは気づいていないが、ここで私はを利用していた場合戦略パターン私は静的で変化しないコードから、さまざまなアルゴリズムをインターフェースを介して家族に分けます。

編集:ここで使用されている戦略パターンを維持したいと思います。

+0

興味深い提案

public class RunStringEvaluation implements ExecuteBehavior<String> { @Override public void execute() { // some processing System.out.println("Running string evaluation"); } } 

StringEngine

ではなく、私が移動した場合、その後

public class RunNumberEvaluation implements ExecuteBehavior<Number> { @Override public void execute() { // some processing System.out.println("Running numeric evaluation"); } } 

NumberEngine

public class NumberEngine extends Engine<Number> { public NumberEngine() { executeBehavior = new RunNumberEvaluation(); } @Override public void init(String pathToResources) { System.out.println("Initializing NumberEngine with resources "+pathToResources); System.out.println("Successfully initialized NumberEngine!"); } } 

そしてRunStringEngine、 'setExecuteBehavior()'をEngineからサブクラスに継承すると、私は採用しているストラテジパターンを破ることになります – barthelonafan

+0

オブジェクトの向きが新しくなりましたか?あなたが 'setExecuteBehavior()'抽象を作成した場合、継承は維持されます。メソッドがAPIの一部であり、そうでない場合はそうしない場合は、これを行います。 – scottb

+0

私はコードの再利用を述べたはずですが、もっと重要なことに、これは戦略パターンを破るでしょうか? – barthelonafan

答えて

1

まずエンジンの正式なparmae​​terリストに「変数」のクラスを置く:あなたが提供したサンプルコードで

public class StringEngine extends Engine<RunStringEvaluation> { 
    public StringEngine() { 
     executeBehavior = new RunStringEvaluation(); 
    } 

    @Override 
    public void init(String pathToResources) { 
     System.out.println("Initializing StringEngine with resources "+pathToResources); 
     System.out.println("Successfully initialized StringEngine!"); 
    } 
} 

public abstract class Engine<B extends ExecuteBehavior> { 
    B executeBehavior; 

    public void setExecuteBehavior(B executeBehavior) { 
     this.executeBehavior = executeBehavior; 
    } 
    public void performExecution() { 
     executeBehavior.execute(); 
    } 
    public abstract void init(String pathToResources); 
} 

次に、あなたがサブクラスにあなたが望むように定義することができます、あなたはそれを必要としません。 setExecuteBehaviorをサブクラスに移動してプライベートにするだけです。

+0

露出しているセッターは、正しい動作を設定している限り、ユーザーがいつでもエンジンを設定できるので、完璧です。私は、あなたの最初のコードはOPが望んでいる仕事をしていると思います。 – HuStmpHrrr

+0

はい。私のコメントは、サンプルコードでは、ジェネリックのソリューションは必要ないということです。私は_other_コードでは、設定なしの解決策が適切であることを暗示するつもりはありません。 –

+0

@PaulHicksあなたのソリューションは、私が探していたものとまったく同じように見えます。しかし、私はフォローアップの質問があります。私は機能を拡張し、 'RunStringFormatting'のような' StringEngine'にエンジン特有の機能を追加したかったのですが、どのように構文が見えるでしょうか? 'public abstract class Engine 'そして次に 'public class StringEngine extends Engine 'ですか? – barthelonafan

1

これは、ジェネリックを使用して、あなたは完全に右のあなたがしなければならなかったのは、この

まずインターフェイスのようにクラスを変更することである

のためのジェネリックを使用しようとしていたことを実現するためにかなり簡単です

public interface ExecuteBehavior<T> { 
    void execute(); 
} 
その後

抽象実装

public abstract class Engine<T> { 
    ExecuteBehavior<T> executeBehavior; 

    public void setExecuteBehavior(ExecuteBehavior<T> executeBehavior) { 
     this.executeBehavior = executeBehavior; 
    } 
    public void performExecution() { 
     executeBehavior.execute(); 
    } 
    public abstract void init(String pathToResources); 
} 

そして最後にRunNumberEngineとNumberEngine StringEngine

public class StringEngine extends Engine<String> { 
    public StringEngine() { 
     executeBehavior = new RunStringEvaluation(); 
    } 

    @Override 
    public void init(String pathToResources) { 
     System.out.println("Initializing StringEngine with resources "+pathToResources); 
     System.out.println("Successfully initialized StringEngine!"); 
    } 
} 
+0

このソリューションは実際には優れています。 – HuStmpHrrr

+0

私がNumberとStringに言及したとき、私はExecutionBehaviorの実装クラスに関して話していました。しかし、これも有望です – barthelonafan

+0

ジェネリックスを複数のタイプに拡張することは可能でしょうか?たとえば、将来、機能を拡張し、RunStringFormattingなどのStringEngineにエンジン固有の機能を追加したければ、構文はどのようになりますか? public abstract class Engine その後、パブリッククラスStringEngineはEngine を拡張しますか? – barthelonafan

関連する問題