【问题标题】:Java generics: <B extends BaseB> does not match <? extends BaseB>Java 泛型:<B extends BaseB> 不匹配 <?扩展 BaseB>
【发布时间】:2014-10-09 10:06:23
【问题描述】:

我有两个同构类型层次结构。第一个的基类型是 BaseA,第二个的基类型是 BaseB。我知道如何将 BaseB 的任何子类的任何对象转换为其对应的 BaseA 子类型。我想实现一个方法,该方法采用 BaseB 类型的对象确定其类并构造 BaseA 的相应子类型的对象。示例代码:

public interface BaseA...
public interface BaseB...
public class DerA implements BaseA...
public class DerB implements BaseB...
...
public interface Transform<A,B> {
    A toA (B b);
}

public class DerAtoDerB implements Transform<DerA,DerB> {
    DerA toA (DerB b){...}
}

public class Transformations {
    private static Map<Class<?>, Transform<? extends BaseA, ? extends BaseB>> _map = 
        new HashMap<>();
static {
    _map.put(DerB.class, new DerAtoDerB());
    }

public static <B extends BaseB> BaseA transform(B b){
    Transform<? extends BaseA, ? extends BaseB> t = _map.get(b.getClass());
    return t.toA(b); // Compile error: Transform<A,B#2> cannot be applied to given types
}

为什么 &lt;B extends BaseB&gt;&lt;? extends BaseB&gt; 不兼容?另外,如果我尝试像这样实现静态转换方法:

public static BaseA transform(BaseB b){
    Transform<? extends BaseA, ? extends BaseB> t = _map.get(b.getClass());
    return t.toA(b); // Compile error: Transform<A,B> cannot be applied to given types
}

我收到一个编译错误:Transform&lt;A,B&gt; cannot be applied to given types

谁能解释我在泛型方面做错了什么?

【问题讨论】:

标签: java generics


【解决方案1】:

问题是在transform方法中编译器无法知道从映射中得到的B extends BaseBTransform类中的第二个类型参数(? extends BaseB)实际上代表了BaseBsame 子类。没有什么能阻止您在地图中存储不兼容的类型:

_map.put(DerB.class, new AnotherDerAtoAnotherDerB()); // the types don't match

是保证映射中的类型匹配的人,所以你需要通过强制转换它到正确的类型来告诉编译器:

@SuppressWarnings("unchecked")
public static <B extends BaseB> BaseA transform(B b) {
  Transform<? extends BaseA, B> t = 
    (Transform<? extends BaseA, B>)_map.get(b.getClass());
  return t.toA(b);
}

【讨论】:

    【解决方案2】:

    当编译器遇到一个在其类型中带有通配符的变量时,它知道一定有某个 T 与传入的内容相匹配。它不知道 T 代表什么类型,但它可以为该类型创建一个占位符以指的是 T 必须是的类型。该占位符称为特定通配符的捕获。

    我不知道为什么编译器无法确定 capture&lt;? extends BaseB&gt; 可能是 capture&lt;?&gt; extends BaseB,也许是类型擦除?

    我会改为这样实现它:

    interface BaseA {}
    interface BaseB {}
    class DerA implements BaseA {}
    class DerB implements BaseB {}
    
    interface Transform {
        BaseA toA(BaseB b);
    }
    
    class DerAtoDerB implements Transform {
        public BaseA toA(BaseB b) { return new DerA(); }
    }
    
    class Transformations {
        private static Map<Class<?>, Transform> _map =
                new HashMap<>();
    
        static {
            _map.put(DerB.class, new DerAtoDerB());
        }
    
        public static<B extends BaseB> BaseA transform(B b) {
            Transform t = _map.get(b.getClass());
            return t.toA(b);
        }
    }
    

    【讨论】:

    • 不要将原始类型与泛型混合。必须有一个更清洁的解决方案。 :)
    【解决方案3】:

    ? 表示未知类型。

    当变量是 X 类型时,您可以为其分配一个 X 类型的值或 X 的任何子类型,但“? extends X " 意味着别的东西。

    表示存在未知类型,可能是 XX 的任何子类型。这不是一回事。

    例子:

    public static Transform<? extends BaseA, ? extends BaseB> getSomething(){
      // My custom method
      return new Transform<MySubclassOfA, MySubclassOfB>(); // <-- It does not accept BaseB, only MySubclassOfB
    }
    public static BaseA transform(BaseB b){
        Transform<? extends BaseA, ? extends BaseB> t = getSomething();
        return t.toA(b); // <--- THIS IS WRONG, it cannot accept any BaseB, only MySubclassOfB
    }
    

    在示例中,编译器不知道 t 是否承认任何 BaseB 或什么,但我展示了一个不承认的示例。

    【讨论】:

      【解决方案4】:

      这东西编译:

      package com.test;
      
      import java.util.HashMap;
      import java.util.Map;
      
      interface BaseA{}
      interface BaseB{}
      class DerA implements BaseA{}
      class DerB implements BaseB{}
      
      interface Transform<A,B> {
          A toA (B b);
      }
      
      class DerAtoDerB implements Transform<BaseA,BaseB> {
          public DerA toA(DerB b){ return null; }
      
          @Override
          public BaseA toA(BaseB baseB) {
              return null;
          }
      }
      
      public class Transformations {
          private static Map<Class<?>, Transform<? extends BaseA, ? super BaseB>> _map = new HashMap<Class<?>, Transform<? extends BaseA, ? super BaseB>>();
          static {
              _map.put(DerB.class, new DerAtoDerB());
          }
      
          public static <B extends BaseB> BaseA transform(B b){
              Transform<? extends BaseA, ? super BaseB> t = _map.get(b.getClass());
              return t.toA(b);
          }
      }
      

      我对您的代码所做的更改如下:

      1. DerAtoDerB 现在实现 Transform&lt;BaseA,BaseB&gt;,而不是 Transform&lt;DerA,DerB&gt;
      2. Map 的第二个泛型参数的类型已更改为 Transform&lt;? extends BaseA, ? super BaseB&gt; - 注意使用 super 而不是 extends - 这是相反的类型绑定。

      【讨论】:

        【解决方案5】:

        Java 泛型的主要概念:如果 ChildClass 扩展 ParentClass 并不意味着 YourApi 扩展 YourApi。例如:

        NumberTransform<String, ? extends Number> intTransform = new IntegerTransform<String, Integer>(); // work with Integer numbers only
        NumberTransform<String, ? extends Number> longTransform = new LongTransform<String, Long>();      // work with Long numbers only
        
        longTransform.toA((Integer) 1); // you are trying to make this and got compilation error.
        

        为了帮助编译器替换你的 t 初始化:

        Transform<? extends BaseA, B> t = (Transform<? extends BaseA, B>) _map.get(b.getClass());
        

        【讨论】:

        • 这如何回答这个问题?这应该是一个评论。
        • 其实我并不想这样做,因为Transform的具体实现是由参数的类决定的,即我不会将 Integer 传递给期望 Long 的东西。
        • 你写“变换 t = ...”。部分“?扩展 BaseB”表示它可以是 BaseB 的任何子类型。它可以是 DerB、FooB、LambdaB 等...然后将具体的“B b”传递给转换器,从编译器的角度来看,它可以是 Transformer<...>、Transformer<...>等
        • ursa 是对的。 @egelev 编译器无法知道您要做什么,因为您可能正在做 ursa 所说的事情,而该错误正在保护您免受它的侵害。
        • 我试图用其他方式解释它,但它是同一个概念。
        猜你喜欢
        • 2017-05-21
        • 1970-01-01
        • 1970-01-01
        • 2022-07-21
        • 1970-01-01
        • 1970-01-01
        • 2016-11-07
        • 2021-12-18
        • 1970-01-01
        相关资源
        最近更新 更多