whitehat
whitehat

Reputation: 2391

new ArrayList<Set<?>>() - Why is it OK?

If

    new ArrayList<?>();  // is not Legal

And if

    new ArrayList<? extends Number>();   // is not Legal,

Then why is

    new ArrayList<Set<?>>();    // IS Legal

Can someone explain with example.

Upvotes: 1

Views: 519

Answers (5)

yshavit
yshavit

Reputation: 43401

new ArrayList<?>() wouldn't be very useful. It's helpful to think about what the wildcards mean:

  • ? extends Foo means you can't put anything in (except null), but you can get objects out as Foo
  • ? super Foo means you can put Foos in, but you can't get objects out (except as Object)
  • ? is the intersection: you can't put anything in (except null), and you can't get objects out (except as Object)

Note that List<Foo> can be cast to List<? extends Foo>, List<? super Foo> or List<?>, but none of those three can be safely cast to List<Foo>.

So, let's say you created a new ArrayList<?>(). What kind of reference can you assign it to? Only a List<?>. That means you won't be able to put any references in (other than null), and you won't be able to get any references out except as Object. In other words, this generic is slightly less useful than the raw types that generics were meant to replace.

When you see a List<?> or List<? extends Foo>, chances are very good that somebody created it as an un-wildcarded type, and then cast it to the wildcarded type. For instance:

List<? extends Number> getSomeNumbers() {
    List<Number> numbers = new ArrayList<Number>();
    numbers.add(1);
    numbers.add(2L);
    numbers.add(3.14);
    return numbers; // implicit casting to List<? extends Number>
}

In this example, if numbers had been typed as List<? extends Number>, none of the add lines would have compiled, so it wouldn't have been useful. If we had constructed the object as new ArrayList<? extends Number>(), we'd have no choice but to reference it in that useless, wildcarded form.

Similarly, if you had created a new ArrayList<? super Number>(), then the adds would have worked, but you'd be guaranteeing that nobody could ever retrieve values except as Object. You could have done the same by just creating (and referencing) a new ArrayList<Object>().

So then, why is new ArrayList<Set<?>>() allowed? Because it is useful; we can add elements to the list, as well as retrieve them. As we create each set, we'll create it with no wildcard so that we can add to it, and then we'll cast it down and add it to the list.

List<Set<?>> getSomeSets() {
    List<Set<?>> list = new ArrayList<Set<?>>();
    Set<Number> set = new HashSet<Number>();
    set.add(1);
    set.add(2L);
    set.add(3.14);
    Set<?> wildcardSet = set; // allowed, though not actually needed
    list.add(wildcardSet);
    list.add(set); // don't need to go through intermediate wildcardSet
    return list;
}

Upvotes: 0

Mateusz Chromiński
Mateusz Chromiński

Reputation: 2832

This happens, because in your concrete type declaration you're using interface as generic parameter. Think about how it would be used in the future:

List someList = new ArrayList<Set<?>>();    
Set<?> someSet = new HashSet<Integer>();
someList.add(someSet);

In first line - you're defining a list of ArrayList type, that will be holding 'some set' (of any type). Then, in second line you're declaring and defining set of concrete type HashSet. It is casted into Set<?> so there is no problem with adding it to previously created list.

Upvotes: 1

Mark Rotteveel
Mark Rotteveel

Reputation: 109146

When creating the ArrayList you must specify an explicit type to its generic parameter. ? or ? extends Number are not types. Set<?> is a type (namely a Set, where we don't care about its generic type).

Upvotes: 2

user268396
user268396

Reputation: 11996

The answers have been given already, so here's what you would need to do instead:

First of what if you don't care about the type of things the list contains:

ArrayList example1 = new ArrayList(); // any object will do.
ArrayList<Object> example2 = new ArrayList<Object>(); // since everything inherits from Object anyway...

Then what if you want to add arbitrary Number objects:

ArrayList<Number> example3 = new ArrayList<Number>(); // any Number may be added

Lastly, previous answers gloss over the meaning of your last example.

ArrayList<Set<?>> example4 = new ArrayList<Set<?>>();

That one means you construct an ArrayList in which you expect to find/put Sets which may contain objects of arbitrary type. I.e. you don't care about what is in the Set, but you do care that it is Sets you add to your ArrayList.

As a result, within the scope of all operations performed on your newly declared/created ArrayList the part of Set<?> can be freely translated to Set: the result program will have equivalent semantics.

Upvotes: 1

Buhake Sindi
Buhake Sindi

Reputation: 89189

That's because unknown ? cannot be translated to any type.

new ArrayList<?>();

will result in the following (assuming this could compile).

List<?> list = new ArrayList<?>();
list.add(? e); //Java sees this as WTF???

Since this ? is unknown, java translates this to list.add(null e); and this is errornous.

Upvotes: 0

Related Questions