I will try to explain with examples why such errors appear. We have two types with a common ancestor:
interface RecipeChild {} class RecipeElement implements RecipeChild {} class RecipeStep implements RecipeChild {}
and lists of elements of these types:
ArrayList<RecipeElement> elements = new ArrayList<>(); ArrayList<RecipeStep> steps = new ArrayList<>();
We need a method that will insert an element of the specified type into the list of elements of this type (and there is something else to do).
Let's try to get by with the supertype:
void addToList_supertype( RecipeChild element, ArrayList<RecipeChild> array ) { // работает array.add( element ); } void addSome_supertype() { // The method addToList_supertype(RecipeChild, ArrayList<RecipeChild>) // in the type Generics // is not applicable for the arguments (RecipeElement, ArrayList<RecipeElement>) addToList_supertype( new RecipeElement(), elements ); }
The code does not work, because generics are invariant (the inheritance relationship between types in a parameter does not establish an inheritance relationship between parameterized types) and ArrayList<RecipeElement> not a descendant of ArrayList<RecipeChild>
Let's try to make addToList accept lists of RecipeChild heirs:
void addSome_wildcard_extends() { // работает, ура addToList_wildcard_extends( new RecipeElement(), elements ); } void addToList_wildcard_extends( RecipeChild element, ArrayList<? extends RecipeChild> array ) { // но нет... // The method add(capture#1-of ? extends RecipeChild) // in the type ArrayList<capture#1-of ? extends RecipeChild> // is not applicable for the arguments (RecipeChild) array.add( element ); }
ArrayList<? extends RecipeChild> type ArrayList<? extends RecipeChild> ArrayList<? extends RecipeChild> is not a list in which any heirs of RecipeChild , but a list of elements of some type that is a heir of RecipeChild . ArrayList<RecipeElement> is suitable, so calling addToList_wildcard_extends works, but array.add( element ) could add an element of arbitrary type (eg RecipeStep ) to the inheriting RecipeChild , so the compiler denies this call.
We read about PECS, we see there that if the data is passed to the argument ( array.add( element ) ), then the argument is the consumer, and you need to use super . We do:
void addToList_wildcard_super( RecipeChild element, ArrayList<? super RecipeChild> array ) { // работает array.add( element ); } void addSome_wildcard_super() { // The method addToList_wildcard_super(RecipeChild, ArrayList<? super RecipeChild>) // in the type Generics // is not applicable for the arguments (RecipeElement, ArrayList<RecipeElement>) addToList_wildcard_super( new RecipeElement(), elements ); }
array.add earned, because array now a list of elements with some kind of ancestor RecipeChild . Since an heir can be used anywhere an ancestor is used, adding a child to the collection will not break anything. But the addToList call broke because RecipeElement is not an ancestor of RecicpeChild , and the RecipeChild list RecipeChild not appropriate.
Remember what we need. You need to add an object of some type to the list of objects of the same type . Those. we need to declare the type:
// объявляем тип-параметр T, расширяющий RecipeChild <T extends RecipeChild> void addToList_type_parameter( T element, ArrayList<T> array ) { // работает, мы можем добавить элемент типа T в список элементов типа T array.add( element ); } void addSome_type_parameter() { // работает, мы добавляем элемент типа RecipeElement в список RecipeElement addToList_type_parameter( new RecipeElement(), elements ); // не работает, тип элементов списка не совпадает addToList_type_parameter( new RecipeElement(), steps ); }
Then you can again recall PECS and use ArrayList<? super T> ArrayList<? super T> . Then the method will be able to add elements not only to the list of the same type, but also to the lists of ancestors:
<T extends RecipeChild> void addToList_type_parameter_super( T element, ArrayList<? super T> array ) { // работает, мы можем добавить элемент типа T в список элементов типа T array.add( element ); } void addSome_type_parameter_super() { ArrayList<RecipeChild> children = new ArrayList<>(); addToList_type_parameter_super( new RecipeElement(), children ); // работает addToList_type_parameter_super( new RecipeStep(), children ); // работает addToList_type_parameter_super( new RecipeElement(), steps ); // не работает // T == RecipeElement, RecipeStep не является супертипом RecipeElement }
<T extends RecipeChild> void updateArrayOfChilds(T element, ArrayList<T> array)if theelementtype is known at compile time and matches the content type ofarray. If not, then I don't think it will. - zRrr