在下面的代码中,我想从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);
}
}
提前致谢。
答案 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并且您希望返回类型至少为Child,则可以将Parent或Object分配给Child类型的变量。这就是编译器禁止它的原因。