2

そのため、かなり複雑なシステムを機能させようとしています。これが私が試みていることの基本です。

ルール:

abstract class Rule
{
  // stuff
}

class ExampleRule extends Rule
{
  // stuff
}

ハンドラー:

abstract class RuleHandler<T extends Rule>
{
  Class<T> clazz;
  RuleHandler(Class<T> forClass)
  {
    this.clazz = forClass;
  }
  abstract void doStuff(T rule);
}

class ExampleRuleHandler extends RuleHandler<ExampleRule>
{
  ExampleRuleHandler()
  {
    super(ExampleRule.class);
  }
  void doStuff(ExampleRule rule)
  {
    // stuff
  }
}

そして、それらを結び付けます:

class HandlerDispatcher
{
  Map<Class<? extends Rule>, RuleHandler<? extends Rule>> handlers;
  void register(RuleHandler<? extends Rule> handler)
  {
    handlers.put(handler.clazz, handler);
  }
  void doStuff(List<Rule> rules)
  {
    for(Rule rule : rules)
    {
      RuleHandler<? extends Rule> handler = handlers.get(rule.getClass());
      handler.doStuff(rule);
    }
  }
}

class Test
{
  void main()
  {
    HandlerDispatcher hd = new HandlerDispatcher();
    hd.register(new ExampleRuleHandler());
  }
}

これまで、さまざまなパラメーター (ワイルドカード、制限付きなど) のさまざまな組み合わせを試みましたが、型関連のエラーなしでこのコンパイルをまだ取得していません。洞察、解決策、または代替アプローチは大歓迎です。

4

4 に答える 4

4

ジェネリックを実行時に使用しようとしています。コンパイル時に処理する必要がある型がわからない場合 (実際の型であるか、型パラメーター自体であるか)、単純で単純なジェネリックを使用することはできません。これはコンパイル時のみ (またはほとんど) の構造です。

ここでは、物事を一般的かつ動的に処理しようとしています。それは一般的に不可能です。生の型を使用し、型の安全性を考慮してください。

私自身、あなたはこれでトラブルを求めているだけだと思います:

abstract class RuleHandler<T extends Rule>
{
  abstract void doStuff(T rule);
}

これを作るだけです:

abstract class RuleHandler
{
  abstract void doStuff(Rule rule);
}

そして、そのルール ハンドラーが処理できるタイプのみを登録します。

編集

あなたのコメントに基づいて、あなたの目標は、インターフェースのユーザーにあなたのデザインを強制することであるようです. 私は、フィルタリングと処理の概念を分離する必要があるという主張を支持します。

とはいえ、別のオプションは、クラス トークンをハンドラー自体から除外して、登録メソッドを生成することです。

interface RuleHandler<T extends Rule> {
    void doStuff(T rule);
}

//...

public <T> void register(Class<T> type, RuleHandler<? super T> handler) {
   map.put(type, handler);
}

public void process() {
   for ( Rule r : rules ) {
       for(Map.Entry<Class<?>, RuleHandler<?>> entry : map.entrySet() ) {
           if ( entry.getKey().instanceOf(r) ) {
               @SuppressWarnings("unchecked")
               ((RuleHandler)entry.getValue()).doStuff(r);
           }
       }
   }
}

ここでは、生の型 RuleHandler を使用するときに警告を抑制します。mapへのアクセスを調べて、クラスが常にRuleHandler型パラメーターと一致することを確認することにより、検査のみで安全であることを知っています。ただし、これは明らかに、クライアントが呼び出したときにタイプ セーフの警告がなかった場合register()(つまり、呼び出しをパラメータ化した場合register()) にのみ安全です。

(内側の for ループはget、所定の Rule サブクラスのサブクラスに対してハンドラーが見つかるように追加されました)

于 2010-12-08T18:00:25.123 に答える
1

ここで未チェックのキャストを避けることはできません。

handlersマップ内の各エントリは、エントリごとに異なるいくつかのクラスのClass<T>を に関連付けます。のメソッドは、この制限を表していません。RuleHandler<T>TMap

できることは、Mapエントリごとに型の一貫性を強制する のサブクラスを作成し、新しいマップ クラスで未チェックのキャストをすべて実行することです。

例として、GuavaClassToInstanceMapを見てください。

于 2010-12-08T18:00:26.387 に答える
1

それを観察する

  • ハンドラーは非公開で最終的なものです
  • handler. clazz は最終です
  • RuleHandler<T extends Rule>示すRuleHandler<?> === RuleHandler<? extends Rule>

次のコードは正しい (そしてコンパイルする)

abstract class RuleHandler<T extends Rule>
{
  final Class<T> clazz;
  // as before
}

class HandlerDispatcher
{
  private final Map<Class<?>, RuleHandler<?>> handlers;
  void register(RuleHandler<?> handler)
  {
    handlers.put(handler.clazz, handler);
  }
  void doStuff(List<Rule> rules)
  {
    for(Rule rule : rules)
    {
      @SuppressWarnings("unchecked")
      RuleHandler<Rule> handler = (RuleHandler<Rule>) handlers.get(rule.getClass());
      handler.doStuff(rule);
    }
  }
}

class Test
{
  void main()
  {
    HandlerDispatcher hd = new HandlerDispatcher();
    hd.register(new ExampleRuleHandler());

    RuleHandler<?> handler = new ExampleRuleHandler();
    hd.register(handler);
  }
}
于 2010-12-09T11:39:08.033 に答える
0

問題はこの行です。

RuleHandler<? extends Rule> handler = handlers.get(rule.getClass());

コンパイラは <? ルールのクラスの正しいハンドラーを検索したため、 extends Rule> は正しいクラスです。あなたはと置き換えることができます

RuleHandler handler = handlers.get(rule.getClass());

コンパイラは実行時に正しい型を選択することを認識していないため、これにより警告が生成されます。これが気になる場合は、クラスに追加できます。

@SuppressWarnings("unchecked")
于 2010-12-08T17:54:54.077 に答える