0

どういうわけか次のことが可能ですか?

interface Foo<T> {
    public void bar(T object);
}

...

public void callBar(Foo<?> foo) {
    foo.bar("Hello world!");
}

明らかに、これはタイプ セーフではありません。Foo<?>この場合、実際にはFoo<String>.

しかし、通常の「チェックされていない」警告ではなく、実際には次のエラーが表示されます: The method bar(capture#1-of ?) in the type Foo is not applied for the arguments (String)

通常、この例外を必要な警告に変えるためにできるキャストがいくつかありますが、どういうわけか今は見つかりません...

何か考えはありますか ( 「これをしないでください!」以外にお願いします)?

編集:

誰もが「これをしないでください」について話し合いたいと思っているようですそれで、私が解決しようとしている問題全体をできるだけエレガントに説明させてください。

イベントごとに 10 億のインターフェイス タイプを宣言する必要のない柔軟なイベントバス システムを作成しようとしています。

EventBus次のようなクラスが必要です。

public class EventBus<GroupType, EventType>  {

  ...

  public void addHandler(EventHandler<GroupType, EventType, ?> handler, GroupType group, EventType... events) {
    //                                                      ^ this is the <?> !!!
    // add handler to list of handlers for group and requested event types
  }


  public <Source> void fireEvent(GroupType group, EventType event, Source source) {
    // call all handlers registered for group and event type
  }
}

インターフェイスEventHandlerは次のようになります。

public interface EventHandler<GroupType, EventType, Source> {
  public void onEvent(GroupType group, EventType event, Source source);
}

そうすれば、次のようにイベント ハンドラーを簡単に記述できます。

public class Handler implements EventHandler<Groups, Events, TextBox> {
  public void onEvent(Groups group, Events event, TextBox source) {
    // Do something with source immediately
  }
}

ここでGroups、 およびEventsは、可能なイベント タイプを記述する列挙型です。

次に、それらを登録します

addHandler(myHandler, Groups.EDIT_BOXES, Events.VALUE_CHANGED, Events.CLEARED, ...);

そして、私はそれらを呼び出すことができます

fireEvent(Groups.EDIT_BOXES, Events.VALUE_CHANGED, myEditField);

私のコードでは、グループ内のすべてのソースが型であり、作成するすべてのハンドラーで自分の人生を型キャストしたくないことを知っていますそのため、ハンドラーに特定のインターフェイスを実装できるようにしたいのですが、次のようにすべてのハンドラーを記述するのではなく、EventBus からの安全でない型キャスト (一度書いて永遠に隠します) で呼び出します。EDIT_BOXESTextBox

public class Handler implements EventHandler<Groups, Events> {
  public void onEvent(Groups group, Events event, Object source) {
    TextBox usableSource = (TextBox) source;
    // Do something with usableSource
  }
}

また、キャストが間違っていると、プログラムはクラッシュしていずれかの方法で書き込みます。ハンドラーに「instanceof」チェックを入れても、何らかの方法でそれをエラーとしてスローする必要があります。私はそれをエレガントに行うことができますが、実際には問題ではありません。このシナリオのエラーは、修正する必要があるコードのバグであり、ユーザーに適切に通知する必要がある実行時のユーザー エラーではないためです。

さて、私は他のライブラリが完全にタイプセーフなイベント システムを実装しているのを見てきましたが、これには多くの場合、可能なすべてのタイプのイベントとすべての可能なタイプのイベント ハンドラー、場合によってはイベントバス自体の関数に対してもインターフェイスを宣言する必要があります。あなたが私に尋ねると、それは価値があるよりもはるかに苦痛です.

私が達成しようとしていることを実行するための明確な方法を持っている人がいるなら、私はうれしく思います. しかし、それが他の方法で実行可能かどうかはわかりません。

4

3 に答える 3

4

これが解決策であると提案します:

public void callBar(Foo<?> foo) {
    ((Foo<String>) foo).bar("Hello world!");
}

これ機能する場合、API の型付けに何か問題があると私は主張します。

callBar1 つの可能性は、 が呼び出されるすべての場所で、foo実際にはFoo<String>. しかし、その場合、(ジェネリック) メソッド シグネチャは正しくありません。次のように宣言する必要があります。

public void callBar(Foo<String> foo) {
    foo.bar("Hello world!");
}

もう 1 つの可能性はbar、ジェネリック型と一致しない引数で呼び出された場合、メソッドが実際に正常に機能することです。例えば

Foo<Integer> f = ...
f.bar("Hello world!");

"Hello world!"時間が間違っているためにランタイムが中断することはありません。その場合、インターフェイスでメソッド シグネチャを正しく宣言していない可能性があります。そのはず:

public void bar(Object object);

3 番目の可能性は、コードがFoo.barメソッドの特定の実装に対して機能し、それらの実装でのみ使用することです。しかし、その場合、コードはこれを反映する必要があります。

public void callBar(Foo<?> foo) {
    if (foo instanceof SomeFooImpl) {
        foo.realBar("Hello world!");
    } else {
        throw InvalidArgument("barring the wrong kind of foo");
    }
}

public class SomeFooImpl<T> implements Foo<T> {
    ...
    public void bar(T object) {
        realBar(object);
    }

    publiC void realBar(Object object) {
        System.out.print("Breaker breaker - come in '" + object + "'"));
    }
}

bar( andをオーバーロードできないことに注意してくださいrealBar。つまり、それらに同じ名前を付けます。メソッドには、異なる消去済みシグネチャが必要です ...)


しかし、肝心なのは、正しい解決策として提案しているものはfoo、基本型と一致しない実際の引数でメソッドが呼び出される可能性があるということです。少なくとも、宣言した型シグネチャの観点からは、それは間違っています。


あなたのコメントに応じて更新します。

最善の解決策はこれを変更することだと思います:

public interface EventHandler<GroupType, EventType, Source> {
    public void onEvent(GroupType group, EventType event, Source source);
}

public interface EventHandler<GroupType, EventType> {
    public void onEvent(GroupType group, EventType event, Object source);
}

これは、特定のハンドラーがsourceオブジェクトを期待する型に型キャストする必要があることを意味します。しかし、これはあなたのEventBus APIにかなり義務付けられています...これは、ハンドラーの登録とディスパッチがハンドラーオブジェクトのソースタイプにとらわれないことを言っているようです。単一のハンドラーが複数のソース タイプからのイベントを処理できるため、おそらく機能的な観点からも優れています。

(コメント: あなたの API 設計は、ジェネリックの洗練された使用に重点を置きすぎているように見えます... イベント バスが提供する必要がある機能のサポートを犠牲にしています。私のイベント バスの概念は、静的な依存関係を回避する必要があるというものです。イベントのサプライヤーと消費者の間. これは、プロデューサーと消費者の間の型の不一致を動的に「処理」できる必要があることを意味します. しかし、ジェネリックの使用は、静的な型の依存関係を (再) 導入しているようです... )

于 2012-11-24T23:54:47.177 に答える
1

ジェネリック インターフェイスがある場合は、メソッドに特定の型でそれを呼び出させないでください。または、特定のタイプを使用せず、ジェネリックのままにします

interface Foo<T> {
    public void bar(T object);
}

class SomeGenericClass {
    public <T> void callGenericBar(Foo<T> foo, T param) {
        foo.bar(param);
    }
    public void callStringBar(Foo<String> foo) {
        foo.bar("Hello");
    }
}

あなたがそれを好きなら

public void callBar(Foo<?> foo) {
    ((Foo<String>) foo).bar("Hello world!");
}

次のコードは正常にコンパイルされます。

interface Foo<T> {
    public void bar(T object);
}

class StringFoo implements Foo<String> {
    public void bar(String object) {
        System.out.println(object);
    }
}

class IntFoo implements Foo<Integer> {
    public void bar(Integer object) {
        System.out.println(object);
    }
}

class TestClass {
    public static void callBar(Foo<?> foo) {
        ((Foo<String>) foo).bar("Hello world!");
    }

    public static void main(String[] args) {
        StringFoo foo1 = new StringFoo();
        IntFoo foo2 = new IntFoo();
        callBar(foo1);
        callBar(foo2);
    }
}

しかし:一度実行すると、

java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer

である必要fooありFoo<String>ます。使用する

public static void callBar(Foo<String> foo) {
    foo.bar("Hello world!");
}

複数のタイプの Foo がある場合は、メソッドのオーバーロードを使用します。

于 2012-11-24T23:38:31.057 に答える
0

気にしないで...ちょうどそれを考え出した:

public void callBar(Foo<?> foo) {
    ((Foo<String>) foo).bar("Hello world!");
}
于 2012-11-24T23:16:24.687 に答える