Reputation: 2391
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
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 Foo
s 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
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
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
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 Set
s 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 Set
s 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
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