【问题标题】:Incompatible types when using upper bounding wildcard使用上限通配符时不兼容的类型
【发布时间】:2018-04-12 09:16:41
【问题描述】:

我对 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 泛型的文档,并试图用谷歌搜索它,但肯定有什么我完全误解了。

【问题讨论】:

    标签: java generics inheritance wildcard


    【解决方案1】:

    这个问题可以简写为为什么

    foo(IModel&lt;List&lt;? extends I&gt;&gt; dataModel)

    不能接受类似的参数

    IModel&lt;List&lt;A&gt;&gt; model

    说明

    List&lt;A&gt;List&lt;? extends I&gt;的子类型,所以可以:

    public void bar(List<? extends I> list);
    List<A> listA;
    bar(listA);
    

    但是,它不会使IModel&lt;List&lt;A&gt;&gt;成为IModel&lt;List&lt;? extends I&gt;&gt;的子类型,就像IModel&lt;Dog&gt; is not a subtype of IModel&lt;Animal&gt;一样,所以你发布的代码无法编译。

    解决方案

    你可以改成:

    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)
    

    让它编译。

    【讨论】:

      【解决方案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 的泛型常见问题解答的Which super-subtype relationships exist among instantiations of generic types? 中很好地解释了详细信息。

      直观地说,关键是List&lt;A&gt; 类型是List&lt;? extends I&gt; 的子类型。但是让该方法只接受List&lt;List&lt;? extends I&gt;&gt; 不允许您传入一个列表,其元素是List&lt;? extends I&gt; 的子类型。为了接受子类型,您必须使用? extends

      (这甚至可以进一步简化:当一个方法接受 List&lt;Number&gt; 时,您不能传入 List&lt;Integer&gt;。但这不会说明 List&lt;A&gt;List&lt;? extends I&gt; 的子类型在此处清楚)

      【讨论】:

      • 你是对的。我在想这种问题是相当哲学的,所以只提供一个伪代码就足够了。下次我会做得更好。
      【解决方案3】:

      有一个方法 method1(Map&lt;I&gt; aMap&gt;) 并且 A 是一个实现的类,我不允许您使用 Map&lt;A&gt; 调用该方法,这是有原因的。

      有方法:

      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 = ...
      

      并在对应的maps和lists中添加有效的A、B、C类型的对象,然后调用函数foo(model)

      【讨论】:

        猜你喜欢
        • 2022-11-23
        • 1970-01-01
        • 1970-01-01
        • 2020-05-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-15
        相关资源
        最近更新 更多