泛型:实例化类型<! - 的泛型类? super someClass,AnotherClass <? extends YetAnotherClass - >&gt;

时间:2011-08-05 20:20:03

标签: java generics

在下面的代码中,我想从Map获得一个“?super SomeInterface”,如何声明“value”的类型以使我能够这样做?

class SomeClass { /* override hashCode & equals appropriately */}
interface SomeInterface<T> { }
interface AnotherInterface { }


class MainClass {
       private final Map<SomeClass, ? super SomeInterface<? extends AnotherInterface>> someMap;
       private final SomeClass someClass = new SomeClass();

       public MainClass() {
          someMap = new HashMap<SomeClass, SomeInterface<? extends AnotherInterface>>();
          someMap.put(someClass, new SomeInterface<AnotherInterface>(){});
       }

       private void getValue(SomeClass someClass) {
          /*
           * I want to get a "? super SomeInterface<? extends AnotherInterface>" from the Map
           * how do I declare the type of "value" to enable me to do so
           * 
           */
          Object value = someMap.get(someClass);
       }

       private
       <T extends SomeInterface<? extends AnotherInterface>>
       T getValue2(SomeClass someClass) {
          T value;
          // the code below does not work
          // Type mismatch: cannot convert from capture#3-of
          // ? super SomeInterface<? extends AnotherInterface> to T
          value = someMap.get(someClass);
        }
    }

提前致谢。

3 个答案:

答案 0 :(得分:3)

Object是唯一可以声明value的内容,因为如果你有一个? super Anything,它可以是Anything的任何超类,一直到对象。因此,您必须将其分配给最常规的类型。

如果你有一个生成一个<? super Something>的泛型类型,那几乎肯定是一个糟糕的设计(我甚至认为该语言不支持它)。那是因为你不能对它产生的东西做任何推断,并且几乎总是没有任何收获(尽管如此,请参阅下面的问题)。 “PECS”是记忆这一点的好助记符:“产生:(使用)延伸,消耗:(使用)超级”。

另见

答案 1 :(得分:1)

对于getValue2方法,编译器无法保证提供的密钥将映射到特定类型T(无论调用者期望什么)。这是因为地图的值是使用通配符声明的,并且无法确定任意? super SomeInterface<? extends AnotherInterface>T,即使它们具有相同的限制。您将需要转换为T(可能在检查后,如果您无法确定它是否会成功)

我在实施以下Map时遇到了同样的问题:

final Map<Class<? extends MyClass>, MyClassMetaInfo<? extends MyClass>> metaMap;

public <T extends MyClass> MyClassMetaInfo<T> getMetaInfo(Class<T> myClass) {
    T metaInfo = (T)metaMap.get(myClass); //need to cast here
    return metaInfo;
}

除此之外,我对你在这里尝试做的事感到困惑,并希望看到一个解释/用例。

答案 2 :(得分:0)

让我们尝试另一种方法。我用类替换了接口,因为我认为这有助于澄清一些事情。我还将它们扩展为演示一个例子。

class SomeClass { /* override hashCode & equals appropriately */ }
class Parent { }    
class Child extends Parent { }

class MainClass {

    private final Map<SomeClass, ? super Child> someMap;
    private final SomeClass someClass = new SomeClass();

    public MainClass() {
        someMap = new HashMap<SomeClass, Child>();     // #1
        // someMap = new HashMap<SomeClass, Parent>(); // #2 , also valid
        // someMap = new HashMap<SomeClass, Object>(); // #3 , also valid
        someMap.put(someClass, new Child());     // #4
        // someMap.put(someClass, new Parent()); // #5 error
        // someMap.put(someClass, new Object()); // #6 error
    }

    private void getValue(SomeClass someClass) {

        Object value = someMap.get(someClass);
    }

    private <T extends Child> T getValue2(SomeClass someClass) {
        // error
        T value = someMap.get(someClass);
    }
}

someMap被定义为保存地图。允许第1行,但也允许#2和#3。因此,您的地图可以容纳包含不同类型对象的地图。现在考虑一下允许操作的位置。 put()的签名如下:

? super Child java.util.Map.put(SomeClass key, ? super Child value)

因此,值可以是Child,Parent或Object类型。但编译器不知道,因为泛型是用类型擦除实现的。因此,如果实际类型是Child,并且您将放置Parent,则这将无效。

get()看起来像这样:

? super Child java.util.Map.get(Object key)

现在是困难的部分。同样,你不知道这是什么实际类型。如上所述,可以使用#1,#2或#3。如果您使用#2或#3并且您希望返回类型至少为Ch​​ild,则可以将Parent或Object分配给Child类型的变量。这就是编译器禁止它的原因。