我的课将获得一个Object类。然后我使用反射来迭代该类的声明字段,并在每个字段上使用ChangeListener
基类注册Property
。
原创' createChangeListener'方法看起来像这样:
private void createChangeListener(Property property) {
property.addListener(new ChangeListener() {
@Override
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
Foo.this.propertyChanged(observable);
}
});
}
然而,这产生了一个不必要的警告:
warning: [unchecked] unchecked call to addListener(ChangeListener<? super T>) as a member of the raw type ObservableValue
property.addListener(new ChangeListener() {
where T is a type-variable:
T extends Object declared in interface ObservableValue
不要被劝阻,我为Property
参数和ChangeListener
提供了通用类型:
private void createChangeListener(Property<Object> property) {
property.addListener(new ChangeListener<Object>() {
@Override
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
Foo.this.propertyChanged(observable);
}
});
}
...只是现在才被告知我已经将我的问题转移到了反射的来源。下面的代码现在已修改为从原始Property<Object>
转换为Property
w / o泛型类型:
if (Property.class.isAssignableFrom(field.getType())) {
createChangeListener((Property<Object>)(field.get(model)));
}
这个以前没有警告的代码现在正在产生头部倾斜:
warning: [unchecked] unchecked cast
createChangeListener((Property<Object>)(field.get(model)));
required: Property<Object>
found: Object
问题:
答案 0 :(得分:2)
将您的字段值转换为Property<Object>
,明确指出Property
泛型参数为Object
,这可能是错误的,编译器无法帮助您。例如,该字段的实际类型为Property<String>
,您可以执行以下操作:
Property<Object> p = (Property<Object>)(field.get(model)); // compiler warning, runtime ok
p.setValue(new Object()); // runtime ok, but now you're probably stored incompatible value
// somewhere much later in completely different piece of code
String value = this.propertyField.getValue(); // sudden ClassCastException
因此,编译器警告您,转换为Property<Object>
您正在假设编译器无法证明,并且您可能实际上有不同的类型,这可能导致将对象置于不正确的状态,这可能会在稍后的某个地方破坏您的程序。
在您的特定情况下,您不想说“我知道通用参数类型是Object
”。你想说的是“我不关心通用参数类型”。对于这种情况,有一个特殊的构造<?>
。你可以在这里使用它:
void createChangeListener(Property<?> property) {
property.addListener(new ChangeListener<Object>() {
@Override
public void changed(ObservableValue<?> observable,
Object oldValue, Object newValue) {
Foo.this.propertyChanged(observable);
}
});
}
你可以安全地施放到Property<?>
而不会发出任何警告。但是现在你根本不能打电话给setValue
,但似乎你根本不需要。
答案 1 :(得分:1)
你需要提高你的仿制技能。
不要试图通过强制转换来解决它,而是尝试通过自己编写泛型代码来解决它:
private <T> void createChangeListener(Property<T> property) {
property.addListener(new ChangeListener<T>() {
@Override
public void changed(ObservableValue<? extends T> observable, T oldValue, T newValue) {
Foo.this.propertyChanged(observable);
}
});
}
此处,编译器可以帮助您进行类型检查。它知道oldValue和newValue将具有某种类型T,并且可以检查您是否做出了不正确的假设。
既然addListener(ChangeListener<? super T>)
也会接受超类型(<? super T>
!)的监听器,以下情况也应该没问题:
private void createChangeListener(Property<?> property) {
property.addListener(new ChangeListener<Object>() {
@Override
public void changed(ObservableValue<?> observable, Object oldValue, Object newValue) {
Foo.this.propertyChanged(observable);
}
});
}
编译器应该能够验证此代码是类型安全。
确保您了解<?>
,<Object>
,<T>
,<? super T>
,<? extends T>
之间的差异,并尝试了解如何在您自己中使用它们码。总是更喜欢更广泛的版本(extends
和super
可以产生重大影响!查看Java集合中的示例。)
在上面的示例中,super
表示您可以将Object
侦听器附加到Property<String>
,因为它是String
的超类型。