I have the following Java class definition:
import java.util.*;
public class Test {
static public void copyTo(Iterator<? extends Number> it, List<? extends Number> out) {
while(it.hasNext())
out.add(it.next());
}
public static void main(String[] args) {
List<Integer> in = new ArrayList<Integer>();
for (int i = 1; i <= 3; i++) {
in.add(i);
}
Iterator<Integer> it = in.iterator();
List<Number> out = new ArrayList<Number>();
copyTo(it, out);
System.out.println(out.size());
}
}
That's it, I define the method copyTo using wildcards in Java. I define List<Number> out but Iterator<Integer> it. My thinking is I can define the iterator as Iterator<? extends Number> and that would type match. However that's not the case:
Test.java:13: error: no suitable method found for add(Number)
out.add(it.next());
^
method List.add(int,CAP#1) is not applicable
(actual and formal argument lists differ in length)
method List.add(CAP#1) is not applicable
(actual argument Number cannot be converted to CAP#1 by method invocation conversion)
method Collection.add(CAP#1) is not applicable
(actual argument Number cannot be converted to CAP#1 by method invocation conversion)
where CAP#1 is a fresh type-variable:
CAP#1 extends Number from capture of ? extends Number
1 error
So I went ahead and I defined yet another definition for the copyTo method:
static public void copyTo(Iterator<? super Integer> it, List<? super Integer> out) {
while(it.hasNext())
out.add(it.next());
}
It doesn't work either. What would be the correct say of using wildcards in this case?
First of all you want to impose a constraint by adding a type variable to the method itself since by using wildcards you can't impose a constraint between the two arguments, then you must thing about variance of the types involved in your method:
you want as an input an Iterator<X> where X is at least the type of the numeric type you want to copy (or a subtype)
you want as output a List where Y is at most the type of the numeric type (or a super type)
These constraints are different and must be expressed differently:
static public <T> void copyTo(Iterator<? extends T> it, List<? super T> out) {
while(it.hasNext())
out.add(it.next());
}
Which is basically "I accept an Iterator of T or a subtype of T and I output to a List of T or a supertype of T"
If a method signature involves two or more wildcards, and the logic of your method requires them to be the same, you need to use a generic type parameter instead of wildcards.
static public <T extends Number> void copyTo(Iterator<? extends T> it, List<? super T> out) {
while(it.hasNext())
out.add(it.next());
}
Here I have used PECS (producer extends, consumer super). out is consuming Ts (so super), whereas the iterator is producing Ts, so extends.
EDIT
As #Cinnam correctly points out in the comments, you can get away with
static void copyTo(Iterator<? extends Integer> it, List<? super Integer> out)
These signatures are effectively equivalent because Integer is final, so any class that is a super class of some class extending Integer must be a super class of Integer.
However, the two signatures are not equivalent as far as the compiler is concerned. You can test this by trying
static <T extends Number> void copyTo1(Iterator<? extends T> it, List<? super T> out) {
copyTo2(it, out); // doesn't compile
}
static void copyTo2(Iterator<? extends Integer> it, List<? super Integer> out) {
copyTo1(it, out);
}
This does not compile, showing that as far as the compiler is concerned, the version with the type parameter is more general.
Related
I just wrote a method that takes two arguments: 1. An array list of any type that extends Number, and 2. a number of the same type. This method should return an array list of all numbers less than the second argument.
My class is called Quiz3FinalQuestion<T extends Comparable>>
My method I wrote looks like this,
public static <T extends Number> ArrayList<T> lessThan(ArrayList<T> lst, T number) {
ArrayList<T> arLst = new ArrayList<>();
for (T obj: lst) {
if (((Comparable<T>) obj).compareTo(number) < 0)
arLst.add(obj);
}
return arLst;
}
It works as expected, but if I didn't have the Java pre-compiler telling me to cast obj to type Comparable, this wouldn't have run. I'm assuming that the number parameter gets cast in the Comparable version of compareTo. So when I add an obj to my arLst, the object should get cast back into type T, right?
Also, are there any simpler ways to compare wrapper class objects of unknown type T?
You can enforce that T also extends Comparable<? super T>:1
public static <T extends Number & Comparable<? super T>>
ArrayList<T> lessThan(ArrayList<T> lst, T number) {
...
}
Then you can remove the cast.
1. See e.g. Explanation of generic <T extends Comparable<? super T>> in collection.sort/ comparable code? for why we don't just use Comparable<T>.
Why does Collections.sort(List<T>) have the signature :
public static <T extends Comparable<? super T>> void sort(List<T> list)
and not :
public static <T extends Comparable<T>> void sort(List<? extends T> list)
I understand that they both would serve the same purpose; so why did the framework developers use the first option?
Or are these declarations really different?
Your proposed signature would probably work in Java-8. However in previous Java versions type inference was not so smart. Consider that you have List<java.sql.Date>. Note that java.sql.Date extends java.util.Date which implements Comparable<java.util.Date>. When you compile
List<java.sql.Date> list = new ArrayList<>();
Collections.sort(list);
It perfectly works in Java-7. Here T is inferred to be java.sql.Date which is actually Comparable<java.util.Date> which is Comparable<? super java.sql.Date>. However let's try your signature:
public static <T extends Comparable<T>> void sort(List<? extends T> list) {}
List<java.sql.Date> list = new ArrayList<>();
sort(list);
Here T should be inferred as java.util.Date. However Java 7 specification does not allow such inference. Hence this code can be compiled with Java-8, but fails when compiled under Java-7:
Main.java:14: error: method sort in class Main cannot be applied to given types;
sort(list);
^
required: List<? extends T>
found: List<Date>
reason: inferred type does not conform to declared bound(s)
inferred: Date
bound(s): Comparable<Date>
where T is a type-variable:
T extends Comparable<T> declared in method <T>sort(List<? extends T>)
1 error
Type inference was greatly improved in Java-8. Separate JLS chapter 18 is dedicated to it now, while in Java-7 the rules were much simpler.
// 0
public static <T extends Comparable<? super T>> void sort0(List<T> list)
// 1
public static <T extends Comparable<T>> void sort1(List<? extends T> list)
These signatures differ because they impose different requirements on the relationship between type T and the type argument to Comparable in the definition of T.
Suppose for example that you have this class:
class A implements Comparable<Object> { ... }
Then if you have
List<A> list = ... ;
sort0(list); // works
sort1(list); // fails
The reason sort1 fails is that there is no type T that is both comparable to itself and that is, or is a supertype of, the list's type.
It turns out that class A is malformed, because objects that are Comparable need to meet certain requirements. In particular reversing the comparison should reverse the sign of the result. We can compare an instance of A to an Object but not vice-versa, so this requirement is violated. But note that this is a requirement of the semantics of Comparable and is not imposed by the type system. Considering only the type system, the two sort declarations really are different.
They differ because ? super T is less restrictive than T. It is a Lower Bounded Wildcard (the linked Java Tutorial says, in part)
The term List<Integer> is more restrictive than List<? super Integer> because the former matches a list of type Integer only, whereas the latter matches a list of any type that is a supertype of Integer.
Replace Integer with T and it means a T or a java.lang.Object.
I'm currently reading Java Generics, and I am a bit stuck when it comes to Wildcards.
I have been given this method from the Collections class:
public void <T> copy(List<? super T> dest, List<? extends T> src) {
for(int i = 0; i < src.size(); i++) {
dest.set(i, src.get(i));
}
}
I have then been told that it is possible to call the method like this:
List<Object> objs = new ArrayList<Object>();
List<Integer> ints = new ArrayList<Integer>();
Collections.copy(objs, ints);
As the type parameter has been left to the compiler to determine, the book says the compiler chooses the type parameter to be Integer.
But how is that possible?
If it were taken to be Integer, this would mean that in the method declaration -
List<? extends T> would translate to List<Integer extends Integer>.
Is this a mistake, or are there different rules when regarding Generics? I have googled around and the majority of results say that a class cannot be a subclass of itself.
No, it's not an error.
? extends Integer means: any class that is or extends Integer (or implements Integer, if Integer was an interface).
The same goes for ? super Integer, which means: any class that is Integer or is a superclass or super-interface of Integer.
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);
}
abstract class Type<K extends Number> {
abstract <K> void use1(Type<K> k); // Compiler error (Type parameter K is not within its bounds)
abstract <K> void use2(Type<? extends K> k); // fine
abstract <K> void use3(Type<? super K> k); // fine
}
The method generic type K shadows the class generic type K, so <K> doesn't match <K extends Number> in use1().The compiler doesn't know anything usefull about new generic type <K> in use2() and use3() but it is still legal to compile . Why <? extends K> (or <? super K>) match <K extends Number>?
The problem you have is that There are two K types. It may be clearer if you rename one.
abstract class Type<N extends Number> {
abstract <K extends Number> void use1(Type<K> k); // fine
abstract <K> void use2(Type<? extends K> k); // fine
abstract <K> void use3(Type<? super K> k); // fine
}
There are cases where you have to provide duplicate information the compiler can infer, and other places where you don't. In Java 7 it has added a <> diamond notation to tell the compiler to infer types it didn't previously.
To illustrate what I mean. Here is different ways to create an instance of a generic class. Some requires the type be given twice, others only once. The compiler can infer the type.
In general, Java doesn't infer types when it might do in most other languages.
class Type<N extends Number> {
private final Class<N> nClass;
Type(Class<N> nClass) {
this.nClass = nClass;
}
static <N extends Number> Type<N> create(Class<N> nClass) {
return new Type<N>(nClass);
}
static void main(String... args) {
// N type is required.
Type<Integer> t1 = new Type<Integer>(Integer.class);
// N type inferred in Java 7.
Type<Integer> t2 = new Type<>(Integer.class);
// type is optional
Type<Integer> t3 = Type.<Integer>create(Integer.class);
// type is inferred
Type<Integer> t4 = create(Integer.class);
}
When you define the method like this:
abstract <K> void use1(Type<K> k);
You're effectively hiding the type K in your class definition. You should be able to define the methods like this:
abstract void use1(Type<K> k);
First of all, let's rewrite it to avoid shadowing:
abstract class Type<N extends Number> {
abstract <K> void use1(Type<K> k);
abstract <K> void use2(Type<? extends K> k);
abstract <K> void use3(Type<? super K> k);
}
In the first method K acts as a type parameter of Type<N extends Number>, thus its value sould comply to the bound of Type's N. However, method declaration doesn't have any restrictions on value of K, therefore it's not legal. It would be legal if you add a necessary restriction on K:
abstract <K extends Number> void use1(Type<K> k);
In the following methods, the actual type parameter of Type is unknown (?), and K imposes additional bound on it, so that there is nothing illegal in these declarations.
Here is a more practical example with the similar declarations:
class MyList<N extends Number> extends ArrayList<N> {}
<K> void add1(MyList<K> a, K b) {
a.add(b); // Given the method declaration, this line is legal, but it
// violates type safety, since object of an arbitrary type K can be
// added to a list that expects Numbers
// Thus, declaration of this method is illegal
}
<K> void add2(MyList<? extends K> a, K b) {
// a.add(b) would be illegal inside this method, so that there is no way
// to violate type safety here, therefore declaration of this method is legal
}
<K> void add3(MyLisy<? super K> a, K b) {
a.add(b); // This line is legal, but it cannot violate type safey, since
// you cannot pass a list that doesn't expect K into this method
}
This is a gray area; javac 7 and 6 disagree; JLS3 is outdated; no idea where's the new spec.