I have the following method:
private void updateEntries(List<Data> data, List<Order> orders) {
// processing code here
List<Order> updates = new ArrayList<>();
for(Data d: data) {
Order anOrder = createOrder(d);
if(anOrder != null) {
updates.add(anOrder);
}
}
orders.clear();
orders.addAll(updates);
}
The method createOrder
will create a subtype of Order
based on the Data
argument. This part compiles.
But the problem is that the calling code of the method will not compile since it is called as follows:
updateEntries(List<Data> data, List<PendingOrder> orders)
and
updateEntries(List<Data> data, List<ExecutedOrder> orders)
both of these are subtypes of Order
and hence the code won't compile on the calling side of the method.
If I update the code as follows:
private void updateEntries(List<Data> data, List<? extends Order> orders) {
// processing code here
List<Order> updates = new ArrayList<>();
for(Data d: data) {
Order anOrder = createOrder(d);
if(anOrder != null) {
updates.add(anOrder);
}
}
orders.clear();
orders.addAll(updates); // <= does not compile
}
The problem is with the line: orders.addAll(updates);
That does not compile of course because the list is declared as List<? extends T>
List<? extends T>
and it tries to avoid adding items of different types in the list.
So how can I use generics so I reuse the function and be able to update the list in the method?
The problem is that updateEntries
gives you back a List<Order>
. You can't add the elements of a List<Order>
to a List<? extends Order>
List<? extends Order>
because the latter - as in your example - may not expect to have any subclass of Order
added to it.
For example, if updateEntries
returns a List
where some/all of the entries are CompletedOrder
s, you shouldn't muddle those up with PendingOrder
s. So the compiler won't allow you to do that.
You need to be able to pass in another "thing" which allows you to ensure that the things returned by updateEntries
will be instances of the element type required by your orders
list.
For example:
private <T extends Order> void updateEntries(List<Data> data, Function<Order, T> orderFn, List<T> orders) {
// processing code here
// Pass in orderFn here too:
List<T> updates = updateEntries(data, orderFn);
orders.clear();
orders.addAll(updates); // <- does not compile
}
Alternatively, if orderFn
is literally just something you can apply to each of your elements in this method, you can do something like:
private <T extends Order> void updateEntries(List<Data> data, Function<Order, T> orderFn, List<T> orders) {
// processing code here
orders.clear();
updateEntries(data).stream().map(orderFn).forEach(orders::add);
}
At the call site, you need to pass in the extra function, eg
updateEntries(listOfData, PendingOrder.class::cast, listOfPendingOrders);
which passes in a Function
that just casts the Order
s to PendingOrder
s.
Please have a look at the difference between <? extends T>
<? extends T>
and <? super T>
<? super T>
.
There is a good answer here: Difference between <? super T> and <? extends T> in Java
If i'am right your code should look like this (but i did not test it):
private <T extends Order> void updateEntries(List<Data> data, List<T> orders) {
// processing code here
List<T> updates = new ArrayList<>();
for(Data d: data) {
T anOrder = (T)createOrder(d);
if(anOrder != null) {
updates.add(anOrder);
}
}
orders.clear();
orders.addAll(updates);
}
The problem is with the createOrder
function that creates a subtype of Order
without being explicit about it. Change that signature to
<T extends Order> T createOrder(Data data)
and be explicit about the type T
in the updateEntries
method:
<T extends Order> void updateEntries(List<Data> data, List<T> orders)
If you cannot change the signature of createOrder
for any reason, wrap the call into a private method and add a cast there.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.