0

Sorry if the question title is confusing. Here is what I mean.

I have a Map<String, ? extends Object> studentDetails = new HashMap<>(); and I want it to hold any random types.

When I put

studentDetails.put("name", "John"); 
studentDetails.put("friendNames", Arrays.aslist("Kenny","Peter","friend3"));
studentDetails.put("courses", new HashSet<String>());
studentDetails.put("age",18);

Compiler gives me :

required type: capture of ? extends Object, Provided: String

required type: capture of ? extends Object, Provided: ArrayList

required type: capture of ? extends Object, Provided: HashSet

required type: capture of ? extends Object, Provided: Integer

Why is it wrong? Shouldn't ? extends Object captures any type? If I change it to Map<String, Object>, then it works.

Any help would be appreciated! Thanks!

  • Because a type of `?` means that you don't know what the type should be. And if you don't know what the type should be, you can't put anything in because the compiler doesn't know it's the right type or not. The generic type for a container that can contain any type of object is `Object`, not `? extends Object`. – khelwood Jul 26 '20 at 21:46
  • If you want the map to be able to hold anything, then use `Object` as 2nd generic parameter. – Turing85 Jul 26 '20 at 21:47
  • @Turing85 but why ? extends Object won't work? My understanding is any type extends Object. – Xinjian Yao Jul 26 '20 at 21:49
  • That is not how bound work. `capture of ? extens Object` and, e.g., `String` are not "the same". In general, I would not recommend to use bounds when declaring a generic variable (due to the problems you encountered), but only when bounding generic paramers of a class and in method parameters (see [the PECS mnemonic](https://stackoverflow.com/questions/2723397/what-is-pecs-producer-extends-consumer-super)) – Turing85 Jul 26 '20 at 21:53
  • 1
    In short, the `?` could be anything. You try to insert a `String`, but the `?` could be a `List`. Or a `Map`. Or a `Car`. You don't know. The only valid value you can pass is `null`. – Johannes Kuhn Jul 26 '20 at 22:02
  • 3
    JohannesKuhn put it nicely. Let me add this: `?` does not mean *any* subtype of `Object`, but rather *an unknown, but specific* subtype of `Object`. – MC Emperor Jul 26 '20 at 22:41
  • @JohannesKuhn so the compiler only knows the declaration of the Map, not the type I passed in? So for example, can I declare it as ? extends Object, then pass a Map, then just add Integer to it? – Xinjian Yao Jul 28 '20 at 07:17

1 Answers1

3

For the same reason you can't do this:

List<Integer> ints = new ArrayList<>();
        
List<? extends Object> objs;

List<Integer> is a subtype of List<? extends Object> so you can assign an instance of List<Integer> to a type List<? extends Object>. But you can't do the following because you would be adding a String into a list of Integers. And the compiler catches it.

objs = ints; // okay
objs.add("abc"); // can't add String to list of ints

The same is true for maps.

Map<String, ? extends Object> map;
Map<String, Integer> intMap = new HashMap<>();

map = intMap; // okay
map.put("abc", "abc");  //oops

The compiler has no idea what your map refers to so you can't add an object to it.

WJS
  • 36,363
  • 4
  • 24
  • 39
  • hi Thanks for the answer, it's kinda partly clear to me now but I still have a little confusion, so in your example, even I did objs.add(new Interger(1)), it still fails. Doesn't the compiler know that it's a `List` since I pass ints which is a `List`? – Xinjian Yao Jul 28 '20 at 07:13