使用上限通配符时类型不兼容
Incompatible types when using upper bounding wildcard
我真的很困惑上限类型在 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 泛型的文档,试图 google 它,但一定有什么我完全误解了。
这个问题可以简写为why
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)
编译。
有方法 method1(Map<I> aMap>)
并且 A 是 class 实现我不允许你用 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)
首先想知道你(一个人)要花多少功夫才能把代码整理成这样的形式:
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
参数是什么,您都应该考虑为此创建一个合适的数据结构,而不是传递如此混乱的深度嵌套的通用映射。
原版编译不通过的原因在其他回答中已经提到了。并且可以通过使用 much 更简单的方法调用的示例来使其更清楚。考虑这个例子:
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 的泛型常见问题解答 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>
的子类型在这里清除)
我真的很困惑上限类型在 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 泛型的文档,试图 google 它,但一定有什么我完全误解了。
这个问题可以简写为why
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)
编译。
有方法 method1(Map<I> aMap>)
并且 A 是 class 实现我不允许你用 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)
首先想知道你(一个人)要花多少功夫才能把代码整理成这样的形式:
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
参数是什么,您都应该考虑为此创建一个合适的数据结构,而不是传递如此混乱的深度嵌套的通用映射。
原版编译不通过的原因在其他回答中已经提到了。并且可以通过使用 much 更简单的方法调用的示例来使其更清楚。考虑这个例子:
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 的泛型常见问题解答 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>
的子类型在这里清除)