我的类将被赋予一个Object类。然后,我使用反射来迭代该类的声明字段,并使用Property
基类在每个字段上注册ChangeListener
。
原来的'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
转换为Property<Object>
,而不是泛型类型:
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
问题:
- ಠ_ಠ
- 考虑到Java的类型擦除限制,我可以使用哪些技术来安全解决这些警告?
- 我是否可以安全地在原始的非类型化方法中抑制未检查的警告?
将字段值转换为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
,但似乎你不需要。
你需要提高你的泛型技能。
与其试图通过强制转换来解决它,不如尝试自己编写泛型代码来解决它:
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
的超类型。