Java:难以从通配符泛型分配给泛型

Java: Difficulty assigning to generic from wildcard generic

我是通配符的新手,对 Java 中的泛型相对缺乏经验,而且我 运行 遇到了分配通配符的问题。

我有一个名为 UserVariable<T> 的自定义通用 class。每个 UserVariable 都知道关于它自己的两件事:它的类型和它的值,这两者都是在构造它时所期望的。准系统定义如下所示:

class UserVariable<T extends Comparable<T>> implements Comparable<UserVariable<T>>
{

private T value;
private final transient Class<T> type;

public UserVariable(T value, Class<T> type)
{
    this.value = value;
    this.type = type;
}

public T getValue()
{
    return value;
}

public Class<T> getType()
{
    return type;
}

}

我将这些 UserVariables 的集合存储在一个 Map 中。我在定义 Map 时使用了通配符,这样我就可以在一个 Map 中存储各种类型的 UserVariables。这里的想法是,我的软件的用户正在创建变量并为它们命名,并且软件可以使用变量名称作为键 store/look 映射中的变量:

class VarsMap {

private ObservableMap<String, UserVariable<?>> userVars = FXCollections.observableHashMap();

public void setVariable(String name, UserVariable<?> variable)
{
    userVars.put(name, variable);
}

public UserVariable<?> getVariable(String name)
{
    return userVars.get(name);
}

}

稍后,我有一个 class 试图检索其中一个变量并存储其类型。我在完成最后一项任务时遇到了问题...

class SimpleComparison<T extends Comparable<T>> {

private StringProperty variableName = new SimpleStringProperty("");
private T comparisonValue;
private transient Class<T> myVarType;

public SimpleComparison(String variableName, T value, VarsMap theVars)
{
    this.myVarType = theVars.getVariable(variableName).getType();  //error here
}

}

this.myVarType 定义为 Class<T>UserVariable<T>getType() 方法应 return 为 Class<T>。此外,每个 UserVariabletype 在构造每个 UserVariable 时存储。但是,最后一次分配给出了以下错误:

类型不匹配:无法从 Class<capture#1-of ?> 转换为 Class<T>

也许这是因为地图是UserVariable<?>的地图?有谁知道我在这里做错了什么?感谢您的帮助!

编辑:最终我想做的是在 SimpleComparison 中有一个名为 evaluate 的方法来执行此操作:

public int evaluate(VarsMap theVars)
{
    UserVariable<T> lhs = theVars.getVariable(variableName);
    UserVariable<T> rhs = new UserVariable<T>(comparisonValue, myVarType);

    return lhs.compareTo(rhs);
}

也就是说,我使用变量名和要比较的值构造了一个 SimpleComparison 对象。稍后我对该对象调用 evaluate,传入当前变量值的 Map,并将变量的当前值(从 Map 获取)与构造时传入的比较值进行比较,并且 return结果。

但是,我不确定对 compareTo 的调用是否有效,因为 lhs 将是 Class<?> 类型,而 rhs 将是 Class<T>。有没有办法按照我打算的方式做到这一点?或者当我将对象放入通配符对象容器(在本例中为 Map)时,对象的类型是否总是会丢失?

问题是您的地图包含具有不同类型参数的值(您可以存储 Class<Integer>Class<String> 等),当您从地图中获取值时您不知道是什么值具有的类型参数并且编译器会引发错误,因为您无法使用不同于 T 的类型参数转换为 Class<T> 值(但您可以通过 [=15= 显式转换为 Class<T> ] 带有不安全转换的编译器警告)。

编译器会严格检查泛型。因此,只要您维护与 onyl 相关的每个通用实例化与同一类型的其他实例化,一切都很好:

MyGeneric<String> s1=...
MyOtherGeneric<String> o1=...
s1.doSomething(o1);

当您尝试混合类型时出现问题:

MyGeneric<String> s1=...
MyOtherGeneric<Double> o1=...
s1.doSomething(o1);    // Error

好的,这很明显。在您的情况下,问题是相似的:您尝试收集几个通用实例化,不管它们基于哪种类型,进入非通用抽象 VarsMap.

因此,当您尝试将非泛型抽象 VarsMap 中包含的对象与泛型对象 Class<T> 混淆时,编译器如何确定它们的类型是否匹配?然后出现编译错误。

我看到的最简单的解决方案:由于您已经按名称匹配 UserVariable 对象,并且我假设每个变量始终具有相同的类型,您可以依靠这个程序进行的检查并假设它们的类型将匹配:只需将一个方法范围的参数添加到方法 getVariable 和一个简单的转换:

public <T extends Comparable<T>> UserVariable<T> getVariable(String name)
{
    return (UserVariable<T>)this.userVars.get(name);
}

... 并在您调用它的任何地方添加正确的类型参数替换:

this.myVarType=theVars.<T> getVariable(variableName).getType();