【问题标题】:Incompatible types error while trying to create a map of maps尝试创建地图时出现不兼容类型错误
【发布时间】:2013-08-28 12:28:28
【问题描述】:

我正在尝试使用ConcurrentSkipListMap 创建地图地图。如果我创建一个简单的地图示例,它似乎很好:

Map<Integer, Integer> mmap2 = new ConcurrentSkipListMap<Integer, Integer>();

一旦我尝试创建地图地图,我会收到 Incompatible types 错误:

Map<Integer,  Map<Integer, Integer>> mmap = 
   new ConcurrentSkipListMap<Integer, ConcurrentSkipListMap<Integer, Integer>>();

如果我切换定义以包含ConcurrentSkipListMap,它的编译没有问题:

Map<Integer,  ConcurrentSkipListMap<Integer, Integer>> mmap = 
   new ConcurrentSkipListMap<Integer, ConcurrentSkipListMap<Integer, Integer>>();

为什么我不能使用Map接口定义地图的地图?

【问题讨论】:

标签: java generics map concurrentskiplistmap


【解决方案1】:

我可以用一个例子来回答这个问题。

Map<Integer, Map<Integer, Integer> mmap = new ConcurrentSkipListMap<Integer, ConcurrentSkipListMap<Integer, Integer>>();

mmap.put(5, new HashMap<Integer, Integer>());

在这种情况下,您是否希望 put 行被允许?如果不允许,那么它会破坏 mmap 的定义。如果允许,则它会破坏右手边。

您已经生成了一行代码,无论它是否有效,都会给您带来矛盾。因此我们不允许这样定义 mmap。

【讨论】:

    【解决方案2】:

    继承不适用于泛型type parameters
    您可以使用如下通配符。

       Map<Integer,  ? extends Map<Integer, Integer>> mmap = new ConcurrentSkipListMap<Integer, ConcurrentSkipListMap<Integer, Integer>>();  
    

    更多信息请阅读java subtyping

    【讨论】:

    • 也许可以解释为什么会这样,类似于我的评论(但有条理)。
    • 您将无法向您的Map 添加任何内容。试试看!
    【解决方案3】:

    Polymorphism 的概念并没有像它们对类一样扩展到 Java 泛型。这就是为什么ConcurrentSkipListMap&lt;Integer, ConcurrentSkipListMap&lt;Integer, Integer&gt;&gt; 不被视为Map&lt;Integer, Map&lt;Integer, Integer&gt;&gt; 的子类型,因此无法分配。

    原因generics 只提供 compile-time 类型安全。在运行时,由于所谓的类型擦除,泛型类型是未知的。所以,基本上 编译器 试图阻止这种情况

    // if this was allowed
    List<Shape> shapes = new ArrayList<Circle>();
    
    // and some place else in your code
    shapes.add(new Square()); // Square now fits in a Circle list
    

    这会破坏ArrayListgeneric 类型并且不会抛出任何错误;因为,哪个 type 有效,哪个无效,在运行时是未知的。但是,如果你说,“嘿,这就是我想要的!Square 进入 Shapes 的列表。” 然后使用 new ArrayList&lt;Shape&gt;() 定义列表编译器会遵守。

    所以,你只需要把你的任务设置为

    Map<Integer,  Map<Integer, Integer>> mmap = 
                      new ConcurrentSkipListMap<Integer, Map<Integer, Integer>>();
    

    即在使用泛型时,更喜欢使用双方一致的接口。

    编辑:(回应@PaulBellora 的反对票)

    您可以将Circle[] 分配给Shape[],但不能将ArrayList&lt;Circle&gt; 分配给ArrayList&lt;Shape&gt;,这是有原因的。而且,原因是如果您的代码尝试通过Shape[] 引用将Square 添加到Circle[],您将在运行时获得ArrayStoreException,因为JVM 会知道数组的实际类型。

    但是,由于类型擦除,相同的运行时类型安全性不能扩展到集合,因此泛型类型不是协变的。如果问题是为什么要删除类型,那么如果在运行时知道它显然会带来好处;答案是使用 Java 5 之前的代码库。

    【讨论】:

    • -1 泛型不是协变的原因不是类型擦除。
    • @PaulBellora,将我的回复添加为更新。请务必就您认为正确的原因做出回应。
    • @TheOtherDownVoter,请注意留下回复。遇到可以支持他们所说的话的人总是很高兴。
    • 泛型不是协变的,这是一个语言设计决定——无疑有助于类型擦除,但只是巧合。回想起来,数组协方差在很大程度上被认为是一个错误。例如,C# 已经具体化了泛型,但泛型仍然不是协变的,因为它会导致类型不安全的代码(参见 this article)。
    • 好吧,显然@Paul 我的回答只适用于 Java。我从来没有想过它会被视为通用(双关语:)。如果您在 Java 的上下文中考虑我的回答,我们会从它的两端说同样的话:由 type erasure 由于现有的预泛型代码和设计决策而引入,因此 键入擦除。我真的希望你能看到。
    【解决方案4】:

    您可以在这里尝试一下,您将在 Map 对象中拥有 Map 引用

    public class GenericTest {
    
        void fun(){
            Map<Integer, Map<Integer, Integer>> mmap = new HashMap<Integer, Map<Integer, Integer>>();
    
            Map<Integer, Integer> map = new HashMap<Integer, Integer>();
    
            mmap.put(5,map);
        }
    }
    

    【讨论】:

    • 没有回答“为什么我不能使用Map 接口定义地图的地图?”的问题
    猜你喜欢
    • 2019-05-07
    • 2019-11-20
    • 1970-01-01
    • 1970-01-01
    • 2012-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-15
    相关资源
    最近更新 更多