简体   繁体   English

Java泛型: <B extends BaseB>不匹配 <? extends BaseB>

[英]Java generics: <B extends BaseB> does not match <? extends BaseB>

I have two isomorphic type hierarchies. 我有两个同构类型的层次结构。 The base type of the first one is BaseA and the base type of the second one is BaseB. 第一个的基本类型是BaseA,第二个的基本类型是BaseB。 I know how to transform any object of any subclass of BaseB to its corresponding subtype of BaseA. 我知道如何将BaseB的任何子类的任何对象转换为其对应的BaseA子类型。 I want to implement a method which takes object of type BaseB determines its class and constructs an object of the corresponding subtype of BaseA. 我想实现一个方法,它接受BaseB类型的对象确定其类并构造相应的BaseA子类型的对象。 Example code: 示例代码:

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
}

Why <B extends BaseB> is not compatible with <? extends BaseB> 为什么<B extends BaseB><? extends BaseB>不兼容<? extends BaseB> <? extends BaseB> ? <? extends BaseB> Also if I try implementing the static transform method like this: 此外,如果我尝试实现这样的静态转换方法:

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
}

I get a compilation error: Transform<A,B> cannot be applied to given types 我得到一个编译错误: Transform<A,B> cannot be applied to given types

Can anyone explain me what I am doing wrong with Generics? 任何人都可以解释我对Generics的错误吗?

The problem is that in the transform method the compiler can't know that the type parameter B extends BaseB and the second type parameter in the Transform class ( ? extends BaseB ) that was gotten from the map actually represent the same subclass of BaseB . 问题是在transform方法中,编译器不能知道类型参数B extends BaseB并且从map获得的Transform类中的第二个类型参数( ? extends BaseB )实际上代表了BaseB相同子类。 Nothing stops you from storing an incompatible type in the map: 没有什么能阻止您在地图中存储不兼容的类型:

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

You are the one who guarantees that the types in the map match, so you need to tell the compiler by casting it to the correct type: 是保证映射中的类型匹配的人,因此您需要通过将编译器转换为正确的类型来告诉编译器:

@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);
}

When the compiler encounters a variable with a wildcard in its type it knows that there must have been some T that matches what was sent in. It does not know what type T represents, but it can create a placeholder for that type to refer to the type that T must be. 当编译器在其类型中遇到带有通配符的变量时,它知道必须有一些匹配发送的T。它不知道T代表什么类型,但它可以为该类型创建一个占位符来引用必须是T的类型。 That placeholder is called the capture of that particular wildcard. 该占位符称为捕获该特定通配符。

I don't know why the compiler can't figure out that capture<? extends BaseB> 我不知道为什么编译器无法弄清楚capture<? extends BaseB> capture<? extends BaseB> could be capture<?> extends BaseB , maybe something with type erasure? capture<? extends BaseB>可以capture<?> extends BaseB ,也许是类型擦除的东西?

I would instead implement it like this: 我会这样实现它:

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);
    }
}

? means unknown type. 意思是未知类型。

When a variable is of type X you can assign it a value of type X or any subtype of X but " ? extends X " means something else. 当一个变量的类型为X的,你可以将其指定类型X的值或X的任何亚型,但“ ? extends X ”是指别的东西。

It means there is an unknown type that may be X or any subtype of X . 这意味着有一个未知的类型,其可以是XX的任何亚型。 It is not the same thing. 这不是一回事。

Example: 例:

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
}

In the example the compiler does not know if t admits any BaseB or what but I shown an example where it doesn't. 在这个例子中,编译器不知道是否允许任何BaseB或者是什么,但是我展示了一个不存在的例子。

This thing compiles: 这个东西编译:

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);
    }
}

The changes I made to your code are the following: 我对您的代码所做的更改如下:

  1. DerAtoDerB now implements Transform<BaseA,BaseB> , instead of Transform<DerA,DerB> DerAtoDerB现在实现Transform<BaseA,BaseB> ,而不是Transform<DerA,DerB>
  2. Type of second generic parameter of Map has changed to Transform<? extends BaseA, ? super BaseB> Map的第二个通用参数的类型已更改为Transform<? extends BaseA, ? super BaseB> Transform<? extends BaseA, ? super BaseB> Transform<? extends BaseA, ? super BaseB> - pay attention to use of super instead of extends - it's the opposite type bound. Transform<? extends BaseA, ? super BaseB> - 注意使用super而不是extends - 它是相反的类型绑定。

Main concept of Java generics: if ChildClass extends ParentClass it DOES NOT mean YourApi<ChildClass> extends YourApi<ParentClass>. Java泛型的主要概念:如果ChildClass扩展ParentClass,它并不意味着YourApi <ChildClass>扩展YourApi <ParentClass>。 Eg: 例如:

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.

To help compiler replace your t initialization: 为了帮助编译器替换你的t初始化:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM