简体   繁体   中英

Adding elements to a generics list

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM