我有一个具有以下结构的函数:
public class TestOrder {
enum Status {
CANCELLED,
EXECUTED
}
static class Data {
Status status;
}
private <T extends Order> void process(List<Data> dataList, List<T> currentUpdates) {
List<T> orders = new ArrayList<>();
for(Data d: dataList) {
Order order = createOrder(d);
if(order != null) {
orders.add((T) order); // <- warning "Unchecked cast Order to T
}
}
// processing
currentUpdates.clear();
currentUpdates.addAll(orders);
}
private Order createOrder(Data data) {
if(data.status == Status.EXECUTED) {
// processing
return new ExecutedOrder(); // implements of Order
}
if(data.status == Status.CANCELLED) {
// processing
return new CancelledOrder(); // implements of Order
}
return null;
}
}
public interface Order {
}
public class CancelledOrder implements Order {
}
public class ExecutedOrder implements Order {
}
有没有办法避免这个警告?实际对象始终是 Order
的子类型,所以我想知道是否可以避免该警告,或者是否存在我忽略的问题。
调用该方法的示例(为了清晰起见是微不足道的):
public static void main(String[] args) {
List<ExecutedOrder> executedOrders = new ArrayList<>();
process(getDataList(), executedOrders);
}
private static List<Data> getDataList() {
return new ArrayList<>();
}
答案 0 :(得分:3)
当您的代码处于当前形式时,没有办法避免警告。考虑这个简化的场景:
public static void main(String[] args) {
List<ExecutedOrder> executedOrders = new ArrayList<>();
process(executedOrders);
ExecutedOrder executedOrder = executedOrders.get(0);
}
private static <T extends Order> void process(List<T> currentUpdates) {
currentUpdates.add((T) createOrder());
}
private static Order createOrder() {
return new CancelledOrder();
}
在这里,访问列表元素时会抛出 ClassCastException
。因此,编译器告诉您强制转换不安全的原因是它是。
尽管您在评论中提到您的 Data
参数可以保证将正确的顺序添加到列表中,但编译器无法验证这一点。这不一定是问题,很多地方都存在不安全的强制转换,但您必须意识到您所做的从根本上来说是不安全的。
我认为 shmosel's answer 提供了最佳选择,因此可能值得重新考虑您对类似事物的处理方法。
答案 1 :(得分:3)
很难根据示例代码提供理想的解决方案,但鉴于您提供的内容,可能值得重构 Data
和 Status
以识别类型:
public class TestOrder {
static abstract class Status<T extends Order> {
public static final Status<CancelledOrder> CANCELLED = new Status<>() {
@Override
public CancelledOrder createOrder() {
return new CancelledOrder();
}
};
public static final Status<ExecutedOrder> EXECUTED = new Status<>() {
@Override
public ExecutedOrder createOrder() {
return new ExecutedOrder();
}
};
public abstract T createOrder();
}
static class Data<T extends Order> {
Status<T> status;
}
private <T extends Order> void process(List<Data<T>> dataList, List<T> currentUpdates) {
List<T> orders = new ArrayList<>();
for(Data d: dataList) {
T order = d.status.createOrder();
if(order != null) {
orders.add(order);
}
}
// processing
currentUpdates.clear();
currentUpdates.addAll(orders);
}
}
答案 2 :(得分:2)
好吧,如果对象始终是 Order
的子类型,那么当您可以拥有 List<T extends Order>
时,我不认为首先需要拥有 List<Order>
。
您使用 Order createOrder
方法丢失了有关类型的信息,然后又希望将其恢复。如果没有特定要求,这种方法毫无意义。
答案 3 :(得分:1)
除了其他答案,如果你想“飞”走演员表:
private <T extends Order> void process(
List<Data> dataList,
Class<T> orderType,
List<T> currentUpdates
) {
List<T> orders = new ArrayList<>();
for (Data d: dataList) {
T order = orderType.cast(createOrder(d)); // check are no longer required
if (order != null) {
orders.add(order);
}
}
但话又说回来,这里的 List<T>
不仅有问题:如果没有类(因此也没有演员表),您可以在 CancelledOrder
中添加 List<ExecutedOrder>
,这会在以下情况下失败你访问它。
我个人会坚持使用 List<Order>
并删除通用的 <T>
,因为它没有意义。
特别是如果您需要传递类型(在这种情况下使用类在某些情况下可以正常工作,而不是所有情况):
List<ExecutedOrder> currentUpdates = ...;
process(dataList, ExecutedOrder.class, currentUpdates);
您首先应该明白为什么要尝试使用 List<ExecutedOrder>
或 List<CancelledOrder>
。