Hello.

So I decided to study Generic a little deeper, because I needed to write my own class. Previously, somehow did not pay attention, but now I want to understand.
The problem is that I can't understand the essence of using <T super SomeClass> . I read a lot of literature, but still did not understand.

There is another opposite wildcard - <T extends SomeClass> . With this, everything is obvious. Powerful concept. We have a superclass, in my case SomeClass . Maybe the interface. But it does not matter. He has his own methods. Who will definitely be! It does not matter how many times was inherited and what methods added. It is important for us that we are confident that any class heir will have methods of the superclass, and we don't care what the class of the heir is called, just call the methods, that's all. It's all clear.

BUT here <T super SomeClass> something is not at all clear what the benefits are. I understand that this is the opposite thing <T extends SomeClass> , that is, just one wildcard restricts the top, the other below. It is not clear why to use it.

I read about the concept of PECS. Examples looked, but did not understand.

What is the use of this class being a superclass of SomeClass . Well, let's say Object -> ParentSomeClass -> SomeClass will have such a hierarchy. Well, all classes will be valid, and there will be no compiler error. BUT what is the use of this? I can implement any methods in ParentSomeClass and in SomeClass . Suppose there are no methods in SomeClass in SomeClass . And in Object , of course, there will be no methods implemented by ParentSomeClass . And what then is the sense, some problems.

I read that it is used when you need to add elements to the collection, for example. But still did not understand the point.

Please explain in as much detail as possible.

1 answer 1

To understand the PECS mnemonic ( provider - extends, consumer - super ), consider the static method from the standard Collections class as a concrete example:

 public static <T> void copy(List<? super T> dest, List<? extends T> src) 

This method copies a list of type Π’ to another list. It uses both types of constraints at once: from the top for the target list and from the bottom for the source.

Let's start with the source. The source can be any child of T types. This is logical, we are not interested in what exactly we are shifting, it is necessary that at least it should be reduced to T. src is for us a supplier ( producer ) of objects, and we allow its type to extend ( extends ). These are the first 2 letters of the PECS mnemonic.

We now turn to the target list. In the future, someone will use this list and, perhaps, somehow process it, and this imposes certain restrictions. It's easier to look at it with an example.

Suppose we have a ΠŸΠΈΡ‚ΠΎΠΌΡ†Π΅Π² class (which is a subclass of Π–ΠΈΠ²ΠΎΡ‚Π½Ρ‹Ρ… ) and a subsidiary of Π‘ΠΎΠ±Π°Ρ‡ΠΎΠ½ΠΎΠΊ and ΠšΠΎΡ‚ΠΈΠΊΠΎΠ² :

 class Animal { void feed() {} } class Pet extends Animal { void call() {} } class Kitty extends Pet{ void mew() {} } class Doge extends Pet{ void bark() {} } 

Now we want to copy from the list of ΠŸΠΈΡ‚ΠΎΠΌΡ†Π΅Π² to the list of ΠŸΠΈΡ‚ΠΎΠΌΡ†Π΅Π² and ΠΏΠΎΠ·Π²Π°Ρ‚ΡŒ them.

 List<Pet> src = ...; List<Pet> dest = new ArrayList<Pet>(); Collections.copy(dest, src); for(Pet p: dest) p.call(); 

While everything is ok. And what if in src were obviously ΠšΠΎΡ‚ΠΈΠΊΠΈ (inherited from ΠŸΠΈΡ‚ΠΎΠΌΡ†Π΅Π² )?

 List<Kitty> src = ...; List<Pet> dest = new ArrayList<Pet>(); Collections.copy(dest, src); for(Pet p: dest) p.call(); 

No problem, ΠšΠΎΡ‚ΠΈΠΊΠΈ respond to the call. When copying to dest we will β€œlose” the knowledge that these were the ΠšΠΎΡ‚ΠΈΠΊΠΈ , but at least they remain ΠŸΠΈΡ‚ΠΎΠΌΡ†Π°ΠΌΠΈ and can still be called.

It is quite obvious that we cannot copy ΠšΠΎΡ‚ΠΈΠΊΠΎΠ² into the Π‘ΠΎΠ±Π°Ρ‡ΠΎΠ½ΠΎΠΊ collection and make them all Π»Π°ΡΡ‚ΡŒ :

 List<Kitty> src = ...; List<Doge> dest = new ArrayList<Doge>(); Collections.copy(dest, src); for(Doge doge: dest) doge.bark(); 

Obvious nonsense, ΠšΠΎΡ‚ΠΈΠΊΠΎΠ² cannot simply be rudely called Π‘ΠΎΠ±Π°Ρ‡ΠΎΠ½ΠΊΠ°ΠΌΠΈ , the type system will not allow doing so, so we could not extend the type of the first parameter in the signature of the copy method:

 public static <T> void copy(List<? extends T> dest, List<? extends T> src); // Ρ‚Π°ΠΊ нСльзя ΠΊΠΎΠΏΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ! 

But what about super ? Let our code continue to copy ΠŸΠΈΡ‚ΠΎΠΌΡ†Π΅Π² , and somewhere else, another code feeds any Π–ΠΈΠ²ΠΎΡ‚Π½Ρ‹Ρ… , and not only pets.

 List<Animal> dest = new ArrayList<Animal>(); ..... List<Kitty> src = ...; Collections.<Pet>copy(dest, src); .... for(Animal a: dest) a.feed(); 

All Π–ΠΈΠ²ΠΎΡ‚Π½Ρ‹Π΅ fed. Note that the target list is a collection of Π–ΠΈΠ²ΠΎΡ‚Π½Ρ‹Ρ… that are the parent class for ΠŸΠΈΡ‚ΠΎΠΌΡ†Π΅Π² . The general concept is such that code that uses the dest list in the future cannot, in its assumptions about its elements, fall below type T , but can arbitrarily abstract to parent types. The dest list is a consumer. we fill it and we know only that its elements are compatible with T , which means that they themselves are T or lie above the hierarchy ( super ). These are the last two letters in the PECS mnemonic.

Hope to clarify something for you.

  • Thank you very much !!!! ___ Oh, it's still something tight, and if we don’t have the class that we put in dest, the feed method? - CROSP 2:49 pm
  • one
    > and if we don’t have a class that we put in the dest of the feed method, where does it go? we put the descendant Π’ in the dest guaranteed. And since T has a feed , then the descendant has one. - Nofate ♦
  • That is, the use of super gives advantages when used in conjunction with extends? And if used separately, then it is useless? - CROSP September
  • 2
    And if separately, it is useless to put the kittens and tables in one list, and then try to feed everyone. - Gorets