This question already has an answer here:
Capturing wildcards in Java generics
(1 answer)
Closed 6 years ago.
<T extends Number> void m1(List<T> list) {
list.add(list.get(0));
}
void m2(List<? extends Number> list) {
list.add(list.get(0));
}
I found difficult to understand the difference between above two methods.
First method m1() compiles successfully, but method m2() produces a compiler error:
The method add(capture#1-of ? extends Number) in the type List<capture#1-of ? extends Number> is not applicable for the arguments (capture#2-of ? extends Number)
Because you cant add an iten on a list of type with upper bounds! You could have a List or List, where one doesnt fit in the other for modifications operations!
List<? extends Number> list = new ArrayList<Integer>();
List<? extends Number> list = new ArrayList<Double>();
List<? extends Number> list = new ArrayList<Long>();
In this case, the variable list could have any type in instance that extends Number. So you can pass it in your method, for example. But there, you don't really know which the type could be. You could have a ArrayList<Integer> and saying to it add a new Double. In compile time makes sense, because Double extends Number, but in runtime the list could no be of this type and throw an Exception.
Just remember that, when you use the upper bounds, <? extends T>, you can't modify the list, just read it! There is the Oracle tutorial (see Wildcards chapter contents - Upper Bounded Wildcards, Lower Bounded Wildcards etc.)
Related
Can someone help me with understanding logic behind this code:
import java.util.List;
import java.util.Arrays;
interface DoubleValue<T> {
public void dv(T e);
}
public class InTest2 {
public static void main(String[] a) {
DoubleValue<Number> func = e -> System.out.println(e.doubleValue());
List<Number> l = Arrays.asList(1, 2, 3);
InTest2.sumOfListNotWorking(l, func);
InTest2.sumOfListWorking(l, func);
InTest2.sumOfListWorkingSuper(l, func);
}
// I thought this should work
public static void sumOfListNotWorking(List<? extends Number> list, DoubleValue<? extends Number> f) {
for (Number n : list)
f.dv(n); // This line give error: The method dv(capture#2-of ? extends Number) in the type DoubleValue<capture#2-of ? extends Number> is not applicable for the arguments (Number)
}
public static void sumOfListWorking(List<? extends Number> list, DoubleValue<Number> f) {
for (Number n : list)
f.dv(n);
}
public static void sumOfListWorkingSuper(List<? extends Number> list, DoubleValue<? super Number> f) {
for (Number n : list)
f.dv(n);
}
}
sumOfListNotWorking:
Here I pass to DoubleValue.dv(<? extends Number> e) a Number and I thought it's OK.
But compiler says that it's not. Why?
I expect with DoubleValue<Number>.dv as e -> e.doubleValue() this should be typical consumer: I get value <? extends Number> and do something with it.
sumOfListWorkingSuper:
Behavior of sumOfListWorkingSuper puzzles me too.
Interface DoubleValue<? super Number> can't be a consumer cause value of <? super Number> can't be changed in place (Number is immutable), but compiler is OK with it.
UPDATE:
Apparently I didn't understand correctly logic of "producer" and "consumer".
sumOfListNotWorking:
This method doesn't compile because I use:
List<? extends Number> list as a producer (in method I take out
values from list with loop).
DoubleValue<? extends Number> as a consumer (in method I pass Number to f.dv(n)), but when I try to pass value Number to f compiler told me that f is defined as a producer (<? extends Number> - have some values of Number or it's children) and it can not accept any values. What if I passed to f argument DoubleValue<Integer> then I would try to put Number where should be Integer - this isn't possible. All this error happens because I define a producer instead of a consumer.
Thanks for pointing out word accept in logic of a consumer and a producer.
List<? extends Number> means "we don't know what this List contains, but whatever it is, it is a descendant of Number or Number itself."
Similarly, DoubleValue<? extends Number> means "we don't know what this DoubleValue can accept to operate with, but whatever it is it accepts, it is a descendant of Number or Number itself".
Here the relevant part is "we don't know what it accepts". We don't know, so you can't give anything to it and expect it will be accepted: because you don't know if it is accepted. The rest of the information is irrelevant. It's relevant in a List<? extends Number> because there at least we know that whatever elements it gives out, can be safely cast to Number after they're given out. But your DoubleValue class doesn't give out anything. It only takes in things. So all that "but we know that it is a descendant of" is completely useless.
<? extends Stuff> is useful on classes that give out objects. It's useless on those that don't give out objects.
<? super Stuff> is useful on classes that take in objects. It's useless on those that don't take in objects.
In addition to #kumesana's answer, this is how you can fix your method:
public static <T extends Number> void sumOfListNotWorking(
List<? extends T> list,
DoubleValue<? super T> f) {
for (T n : list)
f.dv(n);
}
You only needed to make that "unknown" type in wildcards known (T).
Also see the PECS rule. Here, list provides items (i.e. it is a producer), f accepts items (it is a consumer). So type in list should extend T, type in f should super T. This allows passing List<Integer> and DoubleValue<Number>.
This question already has answers here:
Why adding an element (of correct type) into a List through a method of <?> is a compilation error? [duplicate]
(1 answer)
How can I add to List<? extends Number> data structures?
(7 answers)
Closed 9 years ago.
I have:
class Document { ... }
class DocumentCluster extends Document { ... }
And I'm trying to define a set of documents this way:
Set<? extends Document> docs = new HashSet<Document>();
However, when I'm trying to insert a document to my set:
docs.add(d);
I'm getting:
The method add(capture#10-of ? extends Document) in the type Set<capture#10-of ? extends Document> is not applicable for the arguments (Document)
What am I doing wrong?
Because it might only allow objects of type B.
A classic question, answered a million of times. Non-intuitive, but also not a design bug of Java.
Here is the classic example: let A be Fruit.
Can I put an Apple into a Set<? extends Fruit>?
No, because it could be a Set<Banana> which obviously must not contain apples.
? extends Fruit says some specific kind of fruit, and not "any kind of fruit". Then it would be a Set<Fruit> which indeed can take any kind of Fruit.
As a rule of thumb:
when putting ? super Fruit is convenient. It takes "at least" fruits
when getting ? extends Fruit is convenient. It may return only one kind of fruit, but they will all be fruit.
Consider this method:
public static double computeAverage(Collection<? extends Number> col) {
double sum = 0.;
for (Number n : col) {
sum += n.doubleValue();
}
return sum / n.size();
}
This method can work with List<Integer>. Because it does not put Double into the list, but only takes Number objects out.
public static void put(Collection<? super Number> col, Number n) {
col.put(n);
}
Here, we have the opposite case. We need a list that accepts arbitrary numbers (otherwise, we could not put the unspecific number in it). It may accept more, though:
put(new List<Object>(), (Double) 1.);
is valid, because I may put doubles into a list of Objects.
But the compiler correctly prevents put( new List<Integer>(), (Double) 1.).
It can get much messier than that:
public static <I,O> void transfer(Collection<? extends I> input,
Collection<? super O> output,
Converter<? super I, ? extends O> converter) {
for (I inobj : input) {
O outobj = converter.convert(inobj);
output.put(outobj);
}
}
But the compiler may be unable to figure out I and O automatically for you every time.
Set<? extends A> can't be added to because you don't exactly what type you are adding to the collection, only that you are adding SOMETHING that extends A. You can guarentee that when you use get() you will get a class that extends A, but when you use add() because you don't know the definitive type, you can't add to the collection
If you want to take advantage of polymorphism, use Set<A>. This will do the exact same thing - any member that is type compatible, ie, extends, from A can be placed in the Set<K>.
EDIT:
#Anony-Mousse explained this much better with an example.
This question already has answers here:
multiple nested wildcard - arguments not applicable [duplicate]
(2 answers)
Can't cast to to unspecific nested type with generics
(5 answers)
Closed 8 years ago.
I have a function
public static void bar (final List<List<?>> list)
{
}
which I can call with a wildcard (<?>)
bar(new ArrayList<List<?>>());
but not with another type (e.g. String)
// The method bar(List<List<?>>) in the type Foo is not
// applicable for the arguments (ArrayList<List<String>>)
bar(new ArrayList<List<String>>());
However this works for the similar function
public static void foo(List<?> l)
{
}
public static void main(String[] args)
{
// no error
foo(new ArrayList<String>());
}
Can you please explain, why the compiler complains in the first case but not in the second?
You should declare your method as:
private void bar(final List<? extends List<?>> lists) {...}
In this case the call bar(new ArrayList<List<String>>()); would work.
The explanation
In short:
List<SomeType> - The compiler will expect a call with exactly the same type.
List<? extends SomeType> - The compiler will expect a call with a class that is a compatible (sublass) with SomeType.
In your case a definition
void bar (final List<List<?>> list)
will expect a parameter whose definition is exactly List<List<?>>() nestedList;
On the other hand, when you specify your method as:
void bar(final List<? extends List<?>> lists)
Then you're saying that you have a list whose types are upper-bounded by List<?>, so ArrayList<String> would be a valid candidate for the nested list
From Oracle docs:
There is a small but very important difference here: we have replaced
the type List with List. Now drawAll() will
accept lists of any subclass of Shape, so we can now call it on a
List if we want.
List is an example of a bounded wildcard. The ?
stands for an unknown type, just like the wildcards we saw earlier.
However, in this case, we know that this unknown type is in fact a
subtype of Shape. (Note: It could be Shape itself, or some subclass;
it need not literally extend Shape.) We say that Shape is the upper
bound of the wildcard.
Please explain this generic code wildcard compile time error:
//no compile time error.
List<? extends Number> x = new ArrayList<>();
//compile time error.
List<? extends Number> x = new ArrayList<? extends Number>();
It's invalid syntax to instantiate a generic type with wildcards. The type List<? extends Number> means a List of some type that is or extends Number. To create an instance of this type doesn't make sense, because with instantiation you're creating something specific:
new ArrayList<? extends Number>();//compiler:"Wait, what am I creating exactly?"
Generic types with wildcards only make sense for variables and method parameters, because this allows greater freedom in what can be assigned/passed into them.
//compiler:"Okay, so passing in a List<Integer> or a List<Double> are both fine"
public void eatSomeNumbers(List<? extends Number> numbers) {
for (Number number : numbers) {
System.out.println("om nom " + number + " nom");
}
}
Make sure to keep in mind the limitations that come with using wildcards.
List<? extends Number> numList = ...
numList.add(new Integer(3));//compiler:"Nope, cause that might be a List<Double>"
As for your first example, the diamond is a new feature in Java 7 that allows the compiler to infer the type of the new generic instance, based on the type of the variable it's assigned to. In this case:
List<? extends Number> x = new ArrayList<>();
The compiler is most likely inferring new ArrayList<Number>() here, but what's inferred hardly matters, as long as it's a valid assignment to the given variable. This was the reason for the diamond operator being introduced - that specifying the generic type of a new object was redundant, as long some generic type would make it a valid assignment/argument.
This reasoning only makes sense if you remember that generics in Java are a purely compile-time language feature, because of type erasure, and have no meaning at runtime. Wildcards exist only because of this limitation. By contrast, in C# generic type information sticks around at runtime - and generic wildcards don't exist in that language.
Use
List<? extends Number> x = new ArrayList<Number>();
instead.
I can't find any example where wildcards can't be replaced by a generic.
For example:
public void dummy(List<? extends MyObject> list);
is equivalent to
public <T> void dummy(List<T extends MyObject> list);
or
public <T> List<? extends T> dummy2(List<? extends T> list);
is equivalent to
public <T, U> List<U extends T> dummy(List<U extends T> list);
So I don't undertand why wildcard was created as generics is already doing the job. Any ideas or comments?
Nope, it is not always replaceable.
List<? extends Reader> foo();
is not equivalent to
<T> List<T extends Reader> foo();
because you don't know the T when calling foo() (and you cannot know what List<T> will foo() return. The same thing happens in your second example, too.
The demonstration of using wildcards can be found in this (my) answer.
An easy answer is that, deeper level wildcards can't be replaced by a type variable
void foo( List<List<?>> arg )
is very different from
<T>
void foo( List<List<T>> arg)
This is because wildcard capture conversion is only applied to 1st level wildcards. Let's talk about these.
Due to extensive capture conversion, in most places, compiler treats wildcards as if they are type variables. Therefore indeed programmer can replace wildcard with type variables in such places, a sort of manual capture conversion.
Since a type variable created by compiler for capture conversion is not accessible to programmer, this has the restricting effect mentioned by #josefx. For example, compiler treats a List<?> object as a List<W> object; since W is internal to compiler, although it has a method add(W item), there's no way for programmer to invoke it, because he has no item of type W. However, if programmer "manually" converts the wildcard to a type variable T, he can have an item with type T and invoke add(T item) on it.
Another rather random case where wildcard can't be replaced type variable:
class Base
List<? extends Number> foo()
class Derived extends Base
List<Integer> foo()
Here, foo() is overriden with a covariant return type, since List<Integer> is a subtype of List<? extends Number. This won't work if the wildcard is replaced with type variable.
There is a usage difference for your examples.
public <T> List<? extends T> dummy2(List<? extends T> list);
returns a list that can contain an unknown subtype of T so you can get objects of type T out of it but can't add to it.
Example T = Number
List<? extends Number> l = new ArrayList<Integer>(Arrays.asList(new Integer(1)));
//Valid since the values are guaranteed to be a subtype of Number
Number o = l.get(0);
//Invalid since we don't know that the valuetype of the list is Integer
l.add(new Integer(1));
So the wildcard can be used to make some operations invalid, this can be used by an API to restrict an interface.
Example:
//Use to remove unliked numbers, thanks to the wildcard
//it is impossible to add a Number
#Override public void removeNumberCallback(List<? extends Number> list){
list.remove(13);
}