使用上限通配符时类型不兼容

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> 的子类型在这里清除)