我真的很困惑上边界类型在Java泛型中是如何工作的。
假设我有
interface IModel<T>
interface I
class A implements I
class B implements I
class C implements I
然后我有一个带参数的方法如下
foo(IModel<Map<? extends I, Map<? extends I, List<? extends I>>>> dataModel)
调用该方法,如
IModel<Map<A, Map<B, List<C>>>> model = ...
foo(model)
以编译错误结束
Error:(112, 49) java: incompatible types: IModel<java.util.Map<A,java.util.Map<B,java.util.List<C>>>> cannot be converted to IModel<java.util.Map<? extends I,java.util.Map<? extends I,java.util.List<? extends I>>>>
我已经从Oracle网站上阅读了关于Java泛型的文档,试图谷歌它,但必须有一些我完全被误解的东西。
答案 0 :(得分:3)
这个问题可以缩短为什么
foo(IModel<List<? extends I>> dataModel)
不能接受像
这样的论点 IModel<List<A>> model
List<A>
是List<? extends I>
的子类型,所以没关系:
public void bar(List<? extends I> list);
List<A> listA;
bar(listA);
但是,它不会使IModel<List<A>>
成为IModel<List<? extends I>>
的子类型,就像IModel<Dog>
is not a subtype of IModel<Animal>
一样,所以您发布的代码无法编译。
您可以将其更改为:
foo(IModel<? extends Map<? extends I, ? extends Map<? extends I, ? extends List<? extends I>>>> dataModel)
或
<FIRST extends I, SECOND extends I, THIRD extends I> void foo(IModel<Map<FIRST, Map<SECOND, List<THIRD>>>> dataModel)
让它编译。</ p>
答案 1 :(得分:2)
首先,我想知道你(一个人)将代码整理成这种形式会付出多少努力:
import java.util.List;
import java.util.Map;
interface IModel<T> {}
interface I {}
class A implements I {}
class B implements I {}
class C implements I {}
public class UpperBounds
{
public static void main(String[] args)
{
IModel<Map<A, Map<B, List<C>>>> model = null;
foo(model);
}
static void foo(IModel<Map<? extends I, Map<? extends I, List<? extends I>>>> dataModel)
{
}
}
而不是让数百人(想要帮助你)自己做这件事,以便拥有他们可以编译并在他们的IDE中查看的东西。我的意思是, 很难。
话虽如此:从技术上讲,你在这里错过了几个extends
条款。编译好了:
import java.util.List;
import java.util.Map;
interface IModel<T> {}
interface I {}
class A implements I {}
class B implements I {}
class C implements I {}
public class UpperBounds
{
public static void main(String[] args)
{
IModel<Map<A, Map<B, List<C>>>> model = null;
foo(model);
}
static void foo(IModel<? extends Map<? extends I, ? extends Map<? extends I, ? extends List<? extends I>>>> dataModel)
{
}
}
但你应该
像那样实现它。这是模糊不清的。无论这个dataModel
参数是什么,你应该考虑为它创建一个合适的数据结构,而不是传递如此混乱的深嵌套通用映射。
原始版本未编译的原因已在其他答案中提及。通过使用多更简单的方法调用显示示例,可以使其更清晰。考虑这个例子:
interface IModel<T> {}
interface I {}
class A implements I {}
class B implements I {}
class C implements I {}
public class UpperBounds
{
public static void main(String[] args)
{
List<List<A>> lists = null;
exampleA(lists); // Error
exampleB(lists); // Works!
}
static void exampleA(List<List<? extends I>> lists)
{
}
static void exampleB(List<? extends List<? extends I>> lists)
{
}
}
exampleA
方法无法接受给定列表,而exampleB
方法可以接受它。
详细信息在Angelika Langer的generics FAQ的Which super-subtype relationships exist among instantiations of generic types?中得到了很好的解释。
直观地说,关键点是类型List<A>
是List<? extends I>
的子类型。但是,让该方法仅接受List<List<? extends I>>
不允许您传入其元素为List<? extends I>
的子类型的列表。要接受子类型,您必须使用? extends
。
(甚至可以进一步简化:当方法接受List<Number>
时,您无法传入List<Integer>
。但这不会使List<A>
成为子类型List<? extends I>
清楚这里)
答案 2 :(得分:0)
有一个方法method1(Map<I> aMap>)
而A是一个实现我的类,我不允许你用Map<A>
调用该方法,这是有原因的。
有方法:
public static void foo2(IModel<I> dataModel) {
System.out.println("Fooing 2");
}
想象一下这段代码:
IModel<A> simpleModel = new IModel<A>() {};
foo2(simpleModel);
这不应该起作用,因为您为需要泛型类型的方法提供了更具体的类型。现在假设foo2执行以下操作:
public static void foo2(IModel<I> dataModel) {
dataModel = new IModel<B>() {};
System.out.println("Fooing 2 after we change the instance");
}
在这里,您将尝试将IModel设置为有效的IModel - 因为B扩展了I,但是如果您能够使用IModel调用该方法则无法正常工作
创建您的模型,如:
IModel<Map<I, Map<I, List<I>>>> model = ...
并在相应的地图和列表中添加有效的A,B和C类型的对象,然后调用函数foo(model)