3

インターフェイスがあるとします

interface Foo<T> { 
    void foo(T x); 
    T bar() 
}

および不明なパラメーターを持つこのタイプのオブジェクト: Foo<?> baz。その後、私は呼び出すことができますbaz.foo(baz.bar())

ただし、値をコレクションに入れ、後でbaz.bar()呼び出す必要があります。baz.foo()何かのようなもの

List<???> list; // can I tell the compiler this is the same type as baz's wildcard?
list.add(baz.bar());
...
baz.foo(list.get(1));

これも機能しません:

List<Object> list;
list.add(baz.bar());
...
baz.foo((???) list.get(1)); // I can't write down the type I need to cast to

これを行う方法はありますか?

編集:上記は私の実際の状況から単純化されすぎています。私たちが持っていると言う

class Bar {
    private final Foo<?> foo;
    private List<???> list; // the type argument can be selected freely

    Bar(Baz baz) {
        foo = baz.getFoo(); // returns Foo<?>, can't be changed 
    }

    void putBar() {
        list.add(foo.bar());
    }

    void callFoo() {
        foo.foo(list.get(0));
    }
}
4

3 に答える 3

7

次のような汎用メソッドでロジックをラップできます。

public <T> void myMethod(Foo<T> baz) {
  List<T> list; // initialise it...
  list.add(baz.bar());
  // ...
  baz.foo(list.get(1));
}

安全でないキャストに頼ることなく、あなたが望むものを達成するために私が知っている唯一の方法です。

OPの編集後

他の人が述べたように、外部クラスBarは共通のジェネリック型を知る必要がありますT。さらに、 の署名を変更できないという制約があるため、 のコンストラクターでunsafe キャストするBaz.getFoo()必要があると思います。Foo<?>Foo<T>Bar<T>

class Bar<T> {

    private final Foo<T> foo;
    private List<T> list;

    Bar(Baz baz) {
        // Since baz cannot be changed, you will have to
        // unsafe cast Foo<?> to Foo<T> here.
        foo = (Foo<T>) baz.getFoo();
    }

    void putBar() {
        list.add(foo.bar());
    }

    void callFoo() {
        foo.foo(list.get(0));
    }
}

あるいは (Foo、Bar、Baz は実際のユースケースが何をするかを理解するための良い手段ではないため)、次のBarように生成することを避けることもできます:

class Bar {

    private final Foo<Object> foo;
    private List<Object> list;

    Bar(Baz baz) {
        // Since baz cannot be changed, you will have to
        // unsafe cast Foo<?> to Foo<Object> here.
        foo = (Foo<Object>) baz.getFoo();
    }

    void putBar() {
        list.add(foo.bar());
    }

    void callFoo() {
        foo.foo(list.get(0));
    }
}
于 2012-01-20T13:24:46.937 に答える
4

これは厄介な問題です。

解決策は、ワイルドカードの両方の使用を囲むスコープを見つけ、それに型変数を置き、ワイルドカードを型変数への参照に置き換えてから、型変数をバインドすることです。

あなたの場合、クラスが必要なスコープであるように思えます。そう:

public class Whatever<T> {
    private List<T> list;
    private Foo<T> baz;

    public void bazToList() {
        list.add(baz.bar());
    }

    public void listToBaz() {
        baz.foo(list.get(1));
    }
}

問題は、クラスに型変数を追加したことです。現在、このクラスが使用されているすべての場所で、その型について言及する場合は型をバインドする必要があります (so Whatever<String>、またはWhatever<?>)。クラスを使用するコードが、リストに保持するもののタイプをあまり気にしない場合、これは非常に面倒です。

これを回避するには、ジェネリック ホルダー クラスを作成します。何かのようなもの:

public class Whatever {
    private WhateverHolder<?> holder;

    public void bazToList() {
        holder.bazToList();
    }

    public void listToBaz() {
        holder.listToBaz();
    }

    private static class WhateverHolder<T> {
        private List<T> list;
        private Foo<T> baz;

        public void bazToList() {
            list.add(baz.bar());
        }

        public void listToBaz() {
            baz.foo(list.get(1));
        }
    }
}

しかし、それはかなり醜いです。迷惑なジェネリックを回避するために、まったく新しいクラスと新しいオブジェクトの実行時オーバーヘッドを導入しています。

于 2012-01-20T13:39:47.517 に答える
1

あなたの範囲外で何がFoo<T>何であるかわからないということが、どうして可能なのTでしょうか?

            Foo<String> obj = ...; //
            List<String> list = new ArrayList<>();
            list.add(obj.bar());

また

            Foo<MyClass> obj = ...; //
            List<MyClass> list = new ArrayList<>();
            list.add(obj.bar());

編集に応じる

次に、外部クラスもパラメーター化する必要があります

    class OuterClass<S> {

        private List<S> list;

        private Foo<S> baz;

        public void doSomething() {
            Foo<S> obj = ... Consctruct it somehow;
            List<S> list = new ArrayList<>();
            list.add(obj.bar());                
        }


        public static void main(String[] args) throws InterruptedException {


        }
    }

    interface Foo<T> { 
        void foo(T x); 
        T bar();
    }
于 2012-01-20T13:32:04.320 に答える