在这种情况下,我可以避免强制转换为 T 吗?

时间:2021-05-19 20:30:26

标签: java generics casting

我有一个具有以下结构的函数:

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<>();
    }

4 个答案:

答案 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)

很难根据示例代码提供理想的解决方案,但鉴于您提供的内容,可能值得重构 DataStatus 以识别类型:

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>