Reputation: 2946
I have the following code:
public <T extends SomeObject> long doSomething(T someObject){
List<? extends SomeObject> l = new LinkedList<>();
l.add(someObject);
}
this causes a compilation error - telling me that there is no suitable methods found: add(T), why is that?
If l
accept things that extends SomeObject
shouldn't it accept someObject
as it bounds to extend SomeObject
?
Upvotes: 2
Views: 184
Reputation: 2790
I would advise you to use this statement:
List<T> l = new LinkedList<T>();
This is no less type-safe then
List<SomeObject> l = new LinkedList<SomeObject>();
and additionally gives you an opportunity to get objects of type T from the list without casting. T is already SomeObject so no casting required to call methods of SomeObject on T. And all that with less typing!
Back to the problem.
First thing to note is that wildcard type "?" means unknown, this is important. You may, however, specify an upper (? extends) or a lower (? super) constraint to it. You declared a list as "List".
List is known to have objects of SomeObject inside. but! the exact type of objects is unknown.
Compiler can not say if there are instances of "class A extends SomeObject" or instances of "class B extends SomeObject" inside the list. If you call list.get() it can only say that there will be an object of type SomeObject.
SomeObject obj = list.get(1); // Ok
But inserting an object of any(!) type is unsafe because the actual type of elements in the list is unknown.
You could wonder why wildcard type ever exists. It is here to lower restriction in type casting that will be too strict otherwise.
Sample
class A { }
class A2 extends A { }
class B <T> {
void change(T a) { .. };
T read() { .. };
}
If there were no wildcards we would not be able to do this: B<A> b = new B<A2>();
- it does not work.
This is because type conversion from B<A> to B<A2>
is unsafe.
Why? Let's look (copied from http://en.wikipedia.org/wiki/Generics_in_Java)
List<Integer> ints = new ArrayList<Integer>();
ints.add(2);
List<Number> nums = ints; // valid if List<Integer> were a subtype of List<Number>
nums.add(3.14);
Integer x = ints.get(1); // now 3.14 is assigned to an Integer variable!
What is the solution? Sometimes, we want to do such assignments or pass parameters in a general way!
Wildcard type helps here: B<? extends A> b = new B<A2>();
Method B.void change(T a)
is now disabled - this is what your question was about and explained in the first part.
Method B.T read()
is still valid and returns A: A a = b.read();
. Yes, it returns A2 actually but to the caller of b.read()
it's visible as A.
Wildcard types are widely used in Collections Framework.
Upvotes: 1
Reputation: 10285
List<? extends SomeObject> l
What do you mean by that? Of course it will generate an error.
Take this example :SomeObject
is Fruit
, you have 2 derived classes Apple
and Orange
Your list what will it contain? Apple
s or Orange
s? The compiler cannot tell. So it generates error.
If you replace List<? extends SomeObject> l
with List<SomeObject> l
. Then this will work because Apple
and Orange
are both Fruit
.
Upvotes: 12