Java:反射、通用类型和未检查的转换
Java: Reflection, Generic Types, and Unchecked Casts
我的 class 将获得一个对象 class。然后我使用反射遍历 class 的声明字段并在每个字段上注册一个 ChangeListener
Property
base class.
原始的 '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
w/o 转换为 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>
您假设编译器无法证明并且您实际上可能具有不同的类型,这可能导致将对象置于不正确的状态,这可能会在某个地方破坏您的程序稍后。
在您的特定情况下,您不想说 "I know that generic argument type is an Object
"。你想说的是"I don't care about generic argument type"。对于这种情况,有一个特殊的结构 <?>
。您可以在这里使用它:
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
,但似乎您仍然不需要。
您需要提高泛型技能。
与其尝试通过 casts 来解决它,不如尝试通过自己编写 generic 代码来解决它:
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
的超类型。
我的 class 将获得一个对象 class。然后我使用反射遍历 class 的声明字段并在每个字段上注册一个 ChangeListener
Property
base class.
原始的 '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
w/o 转换为 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>
您假设编译器无法证明并且您实际上可能具有不同的类型,这可能导致将对象置于不正确的状态,这可能会在某个地方破坏您的程序稍后。
在您的特定情况下,您不想说 "I know that generic argument type is an Object
"。你想说的是"I don't care about generic argument type"。对于这种情况,有一个特殊的结构 <?>
。您可以在这里使用它:
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
,但似乎您仍然不需要。
您需要提高泛型技能。
与其尝试通过 casts 来解决它,不如尝试通过自己编写 generic 代码来解决它:
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
的超类型。