• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Tim Cooke
  • Liutauras Vilda
  • Jeanne Boyarsky
  • paul wheaton
Sheriffs:
  • Ron McLeod
  • Devaka Cooray
  • Henry Wong
Saloon Keepers:
  • Tim Holloway
  • Stephan van Hulst
  • Carey Brown
  • Tim Moores
  • Mikalai Zaikin
Bartenders:
  • Frits Walraven

Collection. Why is there an 'extends' wildcard for addAll() but a 'super' for removeIf()?

 
Ranch Hand
Posts: 662
3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I was intrigued at this; In java.util.Collection there is an 'extends' wildcard for addAll() but a 'super' for removeIf(). Why couldn't it be the same for both?


 
Marshal
Posts: 79956
396
  • Likes 2
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Please find out about PECS, which, I think, Joshua Bloch introduced. You will find it in Effective Java, 2nd and 3rd editions only. It means Producer=Extends, Consumer=Super. Also find out about generics being invariant, whereas inheritance is covariant.
Briefly, if you are using Collection#addAll(), the argument passed is a Collection used as a producer and the producer has to supply something extending the type in question. If, for example, you have a List<Animal>, you can usually add all the contents of a List<Dog> or a List<Cat> to it. Or of a List<Animal>, because for the purposes of wild‑cards, everything is a subtype of itself.
I think that the question about Collection#removeIf() is different. You cannot expect that a Predicate<Dog> will work instead of a Predicate<Animal>, because a Dog might have a feature not accessible in Animal. You can however find that a Predicate<LivingBeing> can be applied to all Animals, as well as plants or bacteria, because the Animal is sure to have all the features of a LivingBeing. So extends won't work, but super will. Remember that for the purposes of generics, everything is also a supertype of itself.
 
Saloon Keeper
Posts: 15727
368
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The kind of bounds that are appropriate don't depend as much on the method that the generic type is being passed into, but usually more on the generic type itself.

A Predicate should pretty much always be used with the super keyword. It doesn't matter that you're passing it to the removeIf() method or to another method.

The reason for this is that a Predicate is a consumer of the generic type parameter: Predicate.test() consumes an E. You can't get an E out of a Predicate.

For a method argument of type Collection, it's usually the other way around. The Collection acts as a producer of E: the addAll() method has c repeatedly produce an E, which it then adds to its own internal structure.

Types such as Predicate, Consumer and Comparator can only act as consumers of the generic type parameter, so they are almost always used with the super keyword.

Types such as Supplier and Callable can only act as producers of the generic type parameter, so they are almost always used with the extends keyword.

Collection can be used as both a consumer and a producer:

The source Collection produces the elements that are consumed by the sink Collection.

To build on Campbell's example, you could use it like this:
 
Anil Philip
Ranch Hand
Posts: 662
3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Stephan van Hulst wrote:
A Predicate should pretty much always be used with the super keyword.

The reason for this is that a Predicate is a consumer of the generic type parameter: Predicate.test() consumes an E. You can't get an E out of a Predicate.



I understand that Predicate is a consumer but I don't understand why one must use 'super'.
 
Stephan van Hulst
Saloon Keeper
Posts: 15727
368
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Since dogs is a List<Dog>, removeIf() accepts a Predicate<? super Dog>. That means it can also accept a Predicate<Animal>:



Doesn't this seem perfectly reasonable?

If it instead accepted a Predicate<? extends Dog>, then you could pass it a Predicate<GermanShepherd>, which doesn't make sense because the list could contain other dogs than just German shepherds.
 
Wait for it ... wait .... wait .... NOW! Pafiffle! A perfect tiny ad!
Gift giving made easy with the permaculture playing cards
https://coderanch.com/t/777758/Gift-giving-easy-permaculture-playing
reply
    Bookmark Topic Watch Topic
  • New Topic