Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detail about the "super" wildcard in java generics

I have a question regarding generics:

Map<? super String, ? super String> mappa1 = new HashMap<Object,Object>();

with super it's possible to instantiate a HashMap<Object,Object> for a <? super String>. However then you can add only objects which extends String ( in this case only String itself). Why don't they forbid by compilation error as well as happens with the extends wildcard. I mean if once created a Map <Object, Object> it's possible only to add Strings.. why not forcing to create a Map<String, String> in the first place? (like it happens with the extends wildcard)

Again I know the difference between super and extends concerning generics. I would like just to know the details I have aboved-mentioned.

Thanks in advance.

like image 508
Rollerball Avatar asked Jul 08 '13 10:07

Rollerball


3 Answers

Let's use List instead of Map for brevity.

Essentially, practical meaning of extends and super can be defined as follows:

  • List<? extends T> means "a List you can get T from"
  • List<? super T> means "a List you can put T into"

Now you can see that there is nothing special about extends - behavior of extends and super is completely symmetric:

List<? extends Object> a = new ArrayList<String>(); // Valid, you can get an Object from List<String>
List<? extends String> b = new ArrayList<Object>(); // Invalid, there is no guarantee that List<Object> contains only Strings

List<? super String> a = new ArrayList<Object>(); // Valid, you can put a String into List<Object>
List<? super Object> b = new ArrayList<String>(); // Invalid, you cannot put arbitrary Object into List<String>
like image 100
axtavt Avatar answered Nov 09 '22 14:11

axtavt


I think you are thrown off because you picked a collection type. Collections are rarely used as consumers and thus a lower bound (? super X) is not put on their element types. A more appropriate example is predicate.

Consider a method such as <E> List<E> filter(List<? extends E> p, Predicate<? super E> p). It will take a list l and a predicate p and return a new list containing all elements of l which satisfy p.

You could pass in a List<Integer> and a Predicate<Number> which is satisfied by all multiples of 2.5. The Predicate<Number> would become a Predicate<? super Integer>. If it did not, you could not invoke filter as follows.

List<Integer> x = filter(Arrays.asList(1,5,8,10), Predicates.multipleOf(2.5));
like image 4
Ben Schulz Avatar answered Nov 09 '22 14:11

Ben Schulz


Map<? super String, ? super String> mappa1 = new HashMap<Object,Object>();

Since Java Generics are based on type erasure, with this line you didn't create a MashMap<Object,Object>. You just created an instance of the HashMap class; the type parameters get lost immediately after this line of code and all that stays is the type of your mappa1 variable, which doesn't even mention Object. The type of the new expression is assignment-compatible with the type of mappa1 so the compiler allows the assignment.

In general, the type parameters used with new are irrelevant and to address this issue, Java 7 has introduced the diamond operator <>. All that really matters is the type of mappa1, which is is Map<? super String, ? super String>; as far as the rest of your code is concerned, this is the type of the instantiated map.

like image 2
Marko Topolnik Avatar answered Nov 09 '22 13:11

Marko Topolnik