Reputation: 4405
I have a function with the following structure:
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 {
}
Is there a way to avoid that warning? The actual object is always a subtype of Order
so I was wondering if either I could avoid that warning or there is problem I am overlooking.
Example of calling the method (trivial for clarity):
public static void main(String[] args) {
List<ExecutedOrder> executedOrders = new ArrayList<>();
process(getDataList(), executedOrders);
}
private static List<Data> getDataList() {
return new ArrayList<>();
}
Upvotes: 1
Views: 87
Reputation: 5980
There isn't a way to avoid the warning whilst your code is in its current form. Consider this simplified scenario:
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();
}
Here, there will be a ClassCastException
thrown when accessing elements of your list. So the reason the compiler is telling you that the cast is unsafe, is because it is.
Although you mentioned in the comments that your Data
arguments would guarantee the correct orders being added to the list, the compiler has no way to verify this. This isn't necessarily a problem, unsafe casts exist in a great many places, but you have to be aware that what you're doing is fundamentally unsafe.
I think that shmosel's answer gives the best alternative, so it might be worth rethinking your approach to something similar.
Upvotes: 3
Reputation: 11949
In addition to other answers, if you wish to "fly" away the cast:
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);
}
}
But then again, the List<T>
is more than problematic here: without the class (and therefore the cast), you could add a CancelledOrder
in a List<ExecutedOrder>
and this would fail when you access it.
I would personally stick to List<Order>
and remove the generic <T>
as it does not make sense.
Especially if you need to pass the type (using a class in such case works fine in some case, not all case):
List<ExecutedOrder> currentUpdates = ...;
process(dataList, ExecutedOrder.class, currentUpdates);
You should perhaps see why you are trying to use a List<ExecutedOrder>
or List<CancelledOrder>
in the first place.
Upvotes: 1
Reputation: 50716
It's hard to offer an ideal solution based on sample code, but given what you've provided, it might be worth refactoring Data
and Status
to be type-aware:
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);
}
}
Upvotes: 3
Reputation: 133567
Well, if the object is always a subtype of Order
I don't see the requirement of having List<T extends Order>
in the first place when you could have List<Order>
.
You are losing informations about a type by using a Order createOrder
method and then you want it back. This is an approach which makes no sense if there's not a specific requirement.
Upvotes: 2