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。此外,由于您无法更改签名的约束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));
    }
}

问题是您已向该类添加了一个类型变量。现在,无论在哪里使用这个类,任何提及它的类型都需要有一个类型绑定(soWhatever<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 回答