使用上边界通配符时不兼容的类型

我真的很困惑上界类型在Javagenerics中是如何工作的。

比方说我有

interface IModel interface I class A implements I class B implements I class C implements I 

那么我有一个带参数的方法如下

 foo(IModel<Map<? extends I, Map<? extends I, List>>> dataModel) 

调用那个方法就像

 IModel<Map<A, Map<B, List>>> model = ... foo(model) 

以编译错误结束

 Error:(112, 49) java: incompatible types: IModel<java.util.Map<A,java.util.Map<B,java.util.List>>> cannot be converted to IModel<java.util.Map<? extends I,java.util.Map<? extends I,java.util.List>>> 

我已经从Oracle网络上阅读了关于Javagenerics的文档,试图谷歌它,但必须有一些我完全被误解的东西。

这个问题可以缩短为什么

foo(IModel> dataModel)

不能接受这样的论点

IModel> model

说明

ListList的子类型List List ,所以没关系:

 public void bar(List list); List listA; bar(listA); 

但是,它不会使IModel>成为IModel>的子类型IModel> IModel> ,就像IModel不是IModel的子类型一样 ,所以你发布的代码无法编译。

您可以将其更改为:

 foo(IModel>>> dataModel) 

要么

  void foo(IModel>>> dataModel) 

使它编译。

首先,我想知道你(一个人)要以这种forms整理代码需要付出多少努力:

 import java.util.List; import java.util.Map; interface IModel {} interface I {} class A implements I {} class B implements I {} class C implements I {} public class UpperBounds { public static void main(String[] args) { IModel>>> model = null; foo(model); } static void foo(IModel>>> dataModel) { } } 

而不是让数百人(想要帮助你)自己做这件事,以便拥有他们可以编译并在他们的IDE中查看的东西。 我的意思是,这并不难。


话虽如此: 从技术上讲 ,你在这里错过了一些extends条款。 编译好:

 import java.util.List; import java.util.Map; interface IModel {} interface I {} class A implements I {} class B implements I {} class C implements I {} public class UpperBounds { public static void main(String[] args) { IModel>>> model = null; foo(model); } static void foo(IModel>>> dataModel) { } } 

但你应该

像那样实现它。 那太模糊了。 无论这个dataModel参数是什么,你都应该考虑为它创建一个合适的数据结构,而不是传递如此混乱的深嵌套通用映射。


原始版本未编译的原因已在其他答案中提及。 通过使用简单的方法调用显示示例,可以使其更清晰。 考虑这个例子:

 interface IModel {} interface I {} class A implements I {} class B implements I {} class C implements I {} public class UpperBounds { public static void main(String[] args) { List> lists = null; exampleA(lists); // Error exampleB(lists); // Works! } static void exampleA(List> lists) { } static void exampleB(List> lists) { } } 

exampleA方法不能接受给定的列表,而exampleB方法可以接受它。

详细解释了generics类型的实例化中存在哪些超子类型关系? Angelika Langer撰写的仿制药常见问题解答。

直观地说,关键点是类型ListList的子类型List List 。 但是让该方法只接受List> List>不允许你传入一个列表,其元素是List子类型List List 。 为了接受亚型,你必须使用? extends ? extends

(这甚至可以进一步简化:当一个方法接受List ,你就不能传入List 。但这不会使List成为List的子类型List清楚这里)

方法method1(Map aMap>)和A是实现I的类不允许您使用Map调用该方法,这是有原因的。

有方法:

 public static void foo2(IModel dataModel) { System.out.println("Fooing 2"); } 

想象一下这段代码:

 IModel simpleModel = new IModel() {}; foo2(simpleModel); 

这不应该起作用,因为您为需要generics类型的方法提供了更具体的类型。 现在假设foo2执行以下操作:

  public static void foo2(IModel dataModel) { dataModel = new IModel() {}; System.out.println("Fooing 2 after we change the instance"); } 

在这里,您将尝试将IModel设置为有效的IModel – 因为B扩展了I,但是如果您能够使用IModel调用该方法,那么它将无法工作

创建您的模型,如:

 IModel>>> model = ... 

并在相应的地图和列表中添加有效的A,B和C类型的对象,然后调用函数foo(model)