Java:从通配符泛型中赋值给泛型的难度

3

我对Java中的通配符和泛型相对不熟悉,现在遇到了一个赋值问题。

我有一个自定义的泛型类叫做UserVariable<T>。每个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时使用了通配符,这样我就可以将各种类型的UserVariables全部存储在一个Map中。这里的想法是我的软件用户正在创建变量并给它们命名,软件可以使用变量名称作为键来存储/查找Map中的变量。

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 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()方法应该返回一个Class<T>类型。此外,每个UserVariabletype在构造每个UserVariable时被存储。然而,最后一次赋值会给出以下错误:

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

也许这是因为Map是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中获取)与在构造时传入的比较值,并返回结果。

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

2个回答

2
问题在于您的地图包含具有不同类型参数的值(您可以存储Class<Integer>Class<String>等),当您从地图中获取值时,您不知道该值具有什么类型参数,编译器会引发错误,因为您不能将具有与T不同的类型参数的值转换为Class<T>(但是您可以通过(Class<T>) theVars.getVariable(variableName).getType() 显式地将其强制转换为Class<T>,带有不安全强制转换的编译器警告)。

谢谢回复。我在原帖中添加了一个后续问题。您知道我尝试做的事情是否可能吗? - skrilmps
例如,当您从映射中获取类型参数时,您无法知道其是什么类型,但对象本身知道自己的类型,因为它在构造时将其存储为私有变量,对吧?是否可能利用这些信息? - skrilmps

1

泛型由编译器严格检查。因此,只要您将每个通用实例化与同一类型的其他实例化相关联,就可以保持一切正常:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接