Java 通用以避免 ClassCastException
Java generic to avoid ClassCastException
这是我正在处理的一段代码。我有一个可以 return 指定超类型的回调对象。 Vehicle
是其中之一 类。这些 类 用作其他 类 的超类型,例如 Car
和 Train
。
interface CompletionCallBack<Result> {
void onCompletion(Result result);
}
class Vehicle {
Map<String, Object> attributes;
}
class Car extends Vehicle {
public String getMake(){
return attributes.get("make").toString();
}
}
class Train extends Vehicle {
public String getMake(){
return attributes.get("size").toString();
}
}
public class Main {
static void execute(CompletionCallBack<Vehicle> callBack) {
callBack.onCompletion(new Vehicle());
}
public static void main(String[] args) {
execute(new CompletionCallBack<Vehicle>() {
@Override
public void onCompletion(Vehicle result) {
Car car = (Car) result; //Exception
}
});
execute(new CompletionCallBack<Vehicle>() {
@Override
public void onCompletion(Vehicle result) {
Train train = (Train) result; //Exception
}
});
}
}
这里我得到 ClassCastException,因为 Vehicule 不能直接转换为 Car 或 Train。我想调用 execute
给它一个像 Car
或 Train
这样的类型:
public class Main {
static void executeGeneric(CompletionCallBack<? extends Vehicle> callBack) {
callBack.onCompletion(new Vehicle()); //compilation error
}
public static void main(String[] args) {
executeGeneric(new CompletionCallBack<Car>() {
@Override
public void onCompletion(Car car) {
//I receive a car here
}
});
}
}
我正在尝试这样做,因为在我的例子中,Vehicle
包含一个属性映射和一个 Car
车辆,该车辆在该列表中有一些键值对。火车还有其他人。我的 Car
和 Train
对象提供简单的 getter,它知道从 Map
.
检索数据的特定键
我能够做到的唯一方法是删除父子关系并围绕 Vehicle
为我的 Train
和 Car
[=37= 构建装饰器].
您可以使用 instanceof
或 YourClass.class.isInstance(OtherClass);
进行测试,也许您可以创建一个接口来获取所需的值并进行测试。
问题是您有一种适用于任何车辆的机制,无需区分汽车和火车 - 但您希望汽车和火车有不同的行为。
所以有几种解决方法:
1) 拆分机制,以便您对汽车和火车有不同的、类型化的回调
2) 使用多态性 - 在 Vehicle 中提供您在 Car 和 Train 中以不同方式覆盖的方法,并在回调中调用这些方法。
3) 如果所有其他方法都失败了,请使用 instanceof
检测对象的实际类型,然后再将其投射到汽车或火车。
照原样,您要求 new Vehicle()
生成 Car
- 任何编译器或转换都无法完成此工作。
您可能想要的是:
interface CompletionCallback<C> {
Class<C> getType();
void onCompletion(C object);
}
在调用回调之前需要检查类型的位置:
for (CompletionCallback<?> c : callBacks) {
if (c.getType().isAssignableFrom(vehicle)) {
// Perform some ugly casting here
}
}
或者您可以使用 instanceof
.
在您的回调中进行类型检查
Javas EventListenerList
也做了类似的事情。同样,内部代码涉及丑陋的转换。但是 API 很好,因为您可以使用以下类型注册回调:
addCallback(Car.class, new Callback<Car>() {...});
addCallback(Submarine.class, new Callback<Submarine>() { ... });
如果我没记错的话,它在内部将 class 和回调存储在 Object[]
数组中,并且确实使用了转换。但是这个模式很好:用类型注册。
这是我正在处理的一段代码。我有一个可以 return 指定超类型的回调对象。 Vehicle
是其中之一 类。这些 类 用作其他 类 的超类型,例如 Car
和 Train
。
interface CompletionCallBack<Result> {
void onCompletion(Result result);
}
class Vehicle {
Map<String, Object> attributes;
}
class Car extends Vehicle {
public String getMake(){
return attributes.get("make").toString();
}
}
class Train extends Vehicle {
public String getMake(){
return attributes.get("size").toString();
}
}
public class Main {
static void execute(CompletionCallBack<Vehicle> callBack) {
callBack.onCompletion(new Vehicle());
}
public static void main(String[] args) {
execute(new CompletionCallBack<Vehicle>() {
@Override
public void onCompletion(Vehicle result) {
Car car = (Car) result; //Exception
}
});
execute(new CompletionCallBack<Vehicle>() {
@Override
public void onCompletion(Vehicle result) {
Train train = (Train) result; //Exception
}
});
}
}
这里我得到 ClassCastException,因为 Vehicule 不能直接转换为 Car 或 Train。我想调用 execute
给它一个像 Car
或 Train
这样的类型:
public class Main {
static void executeGeneric(CompletionCallBack<? extends Vehicle> callBack) {
callBack.onCompletion(new Vehicle()); //compilation error
}
public static void main(String[] args) {
executeGeneric(new CompletionCallBack<Car>() {
@Override
public void onCompletion(Car car) {
//I receive a car here
}
});
}
}
我正在尝试这样做,因为在我的例子中,Vehicle
包含一个属性映射和一个 Car
车辆,该车辆在该列表中有一些键值对。火车还有其他人。我的 Car
和 Train
对象提供简单的 getter,它知道从 Map
.
我能够做到的唯一方法是删除父子关系并围绕 Vehicle
为我的 Train
和 Car
[=37= 构建装饰器].
您可以使用 instanceof
或 YourClass.class.isInstance(OtherClass);
进行测试,也许您可以创建一个接口来获取所需的值并进行测试。
问题是您有一种适用于任何车辆的机制,无需区分汽车和火车 - 但您希望汽车和火车有不同的行为。
所以有几种解决方法:
1) 拆分机制,以便您对汽车和火车有不同的、类型化的回调
2) 使用多态性 - 在 Vehicle 中提供您在 Car 和 Train 中以不同方式覆盖的方法,并在回调中调用这些方法。
3) 如果所有其他方法都失败了,请使用 instanceof
检测对象的实际类型,然后再将其投射到汽车或火车。
照原样,您要求 new Vehicle()
生成 Car
- 任何编译器或转换都无法完成此工作。
您可能想要的是:
interface CompletionCallback<C> {
Class<C> getType();
void onCompletion(C object);
}
在调用回调之前需要检查类型的位置:
for (CompletionCallback<?> c : callBacks) {
if (c.getType().isAssignableFrom(vehicle)) {
// Perform some ugly casting here
}
}
或者您可以使用 instanceof
.
Javas EventListenerList
也做了类似的事情。同样,内部代码涉及丑陋的转换。但是 API 很好,因为您可以使用以下类型注册回调:
addCallback(Car.class, new Callback<Car>() {...});
addCallback(Submarine.class, new Callback<Submarine>() { ... });
如果我没记错的话,它在内部将 class 和回调存储在 Object[]
数组中,并且确实使用了转换。但是这个模式很好:用类型注册。