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.
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>.
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.
Suppose I have a method "mix" that takes two Lists of possibly different types T and S and returns a single List containing the elements of both. For type-safety, I'd like to specify that the returned List is of a type R, where R is a supertype common to both T and S. For example:
List<Number> foo = mix(
Arrays.asList<Integer>(1, 2, 3),
Arrays.asList<Double>(1.0, 2.0, 3.0)
);
To specify this, I could declare the method as
static <R, T extends R, S extends R> List<R> mix(List<T> ts, List<S> ss)
But what if I want to make mix an instance method instead of static, on the class List2<T>?
<R, T extends R, S extends R> List<R> mix ...
shadows the <T> on the instance of List2, so that's no good.
<R, T extends S&T, S extends R> List<R> mix ...
solves the shadowing problem, but isn't accepted by the compiler
<R super T, S extends R> List<R> mix ...
is rejected by the compiler because lower-bounded wildcards can't be stored in a named variable (only used in ? super X expressions)
I could move the arguments to the class itself, like List2<R, T extends R, S extends R>, but the type information really has no business being on the instance level, because it's only used for one method call, and you would have to re-cast the object every time you wanted to invoke the method on different arguments.
As far as I can tell, there's no way to do this with generics. The best I can do would be to return a raw List2 and cast it at the callsite, like before generics were introduced. Does anybody have a better solution?
As noted in the question and in the comments, the following signature would be ideal:
<R super T, S extends R> List<R> mix(List<S> otherList)
But of course, R super T is not allowed by the language (note that polygenelubricants's answer on the linked post is wrong - there are use cases for this syntax, as your question demonstrates).
There's no way to win here - you only have one of several workarounds to choose from:
Resort to using a signature with raw types. Don't do this.
Keep mix a static method. This is actually a decent option, unless it needs to be part of your class's interface for polymorphism-related reasons, or you plan for mix to be such a commonly used method that you think keeping it static is unnacceptable.
Settle with the signature of mix being overly restrictive, and document that certain unchecked casts will be necessary on the part of the caller. This is similar to what Guava's Optional.or had to do. From that method's documentation:
Note about generics: The signature public T or(T defaultValue) is overly restrictive. However, the ideal signature, public <S super T> S or(S), is not legal Java. As a result, some sensible operations involving subtypes are compile errors:
Optional<Integer> optionalInt = getSomeOptionalInt();
Number value = optionalInt.or(0.5); // error
As a workaround, it is always safe to cast an Optional<? extends T> to Optional<T>. Casting [the above Optional instance] to Optional<Number> (where Number is the desired output type) solves the problem:
Optional<Number> optionalInt = (Optional) getSomeOptionalInt();
Number value = optionalInt.or(0.5); // fine
Unfortunately for you, it's not always safe to cast List2<? extends T> to List2<T>. For example, casting a List2<Integer> to a List2<Number> could permit a Double to be added to something that was only supposed to hold Integers and lead to unexpected runtime errors. The exception would be if List2 was immutable (like Optional), but this seems unlikely.
Still, you could get away with such casts if you were careful and documented type-unsafe code with explanations. Assuming mix had the following signature (and implementation, for fun):
List<T> mix(final List<? extends T> otherList) {
final int totalElements = (size() + otherList.size());
final List<T> result = new ArrayList<>(totalElements);
Iterator<? extends T> itr1 = iterator();
Iterator<? extends T> itr2 = otherList.iterator();
while (result.size() < totalElements) {
final T next = (itr1.hasNext() ? itr1 : itr2).next();
result.add(next);
final Iterator<? extends T> temp = itr1;
itr1 = itr2;
itr2 = temp;
}
return result;
}
Then you might have the following call site:
final List2<Integer> ints = new List2<>(Arrays.asList(1, 2, 3));
final List<Double> doubles = Arrays.asList(1.5, 2.5, 3.5);
final List<Number> mixed;
// type-unsafe code within this scope
{
#SuppressWarnings("unchecked") // okay because intsAsNumbers isn't written to
final List2<Number> intsAsNumbers = (List2<Number>)(List2<?>)ints;
mixed = intsAsNumbers.mix(doubles);
}
System.out.println(mixed); // [1, 1.5, 2, 2.5, 3, 3.5]
Again, a settling for a static mix is going to be cleaner and have no risk to type-safety. I would make sure to have very good reasons not to keep it that way.
The only thing I'm not sure in your question is whether you already know of which supertype these subclasses extends, or you want a completely generic method where you'd pass two subtypes of any given superclass.
In the first case, I did something similar recently, with an Abstract Class and several subtypes:
public <V extends Superclass> List<Superclass> mix(List<V> list1, List<V> list2) {
List<Superclass> mixedList;
mixedList.addAll(list1);
mixedList.addAll(list2);
}
The latter case is much more complicated. I'd suggest you rethink your design, since it makes much more sense for the mix method to be in the Superclass or in a class which knows the superclass and its subtypes, since you're returning a List of the Superclass.
If you really want to do this, you would have to refactor List2 to List2 and do the following:
public <R, V extends R> List<R> mix(List<V> list1, List<V> list2) {
List<R> mixedList;
mixedList.addAll(list1);
mixedList.addAll(list2);
return mixedList;
}
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);
}
I have a question on the use of wildcards in Java's generic types: what is the basic difference between List<? extends Set> and List<T extends Set>? When would I use either?
Two reasons:
To avoid unnecessary casts:
You have to use the T variant for cases like this:
public <T extends Set> T firstOf(List<T> l) {
return l.get(0);
}
With ? this would become:
public Set firstOf2(List<? extends Set> l) {
return l.get(0);
}
...which doesn't give the same amount of information to the caller of the firstOf method. The first version allows the caller to do this:
SubSet first = firstOf(listOfSubSet);
while with the second version, you are forced to use a cast to make it compile:
SubSet first = (SubSet)firstOf(listOfSubSet);
To enforce matching argument types:
public <T extends Set> boolean compareSets(List<T> a, List<T> b) {
boolean same = true;
for(T at : a) {
for (T bt: b) {
same &= at.equals(bt);
}
}
return same;
}
There is no direct equivalent using ? instead of T for this. Note that due to Java's single-dispatch, in the above version, the compiler will call at's equals(T) method which may well differ from at's equals(Set) or equals(Object) method.
The difference here is that in the second version, you have a type variable T that refers to the specific subtype of Set that the List contains. You need this in cases where you need to ensure that something else is the same type as the type contained in the list. A couple simple examples:
// want to ensure that the method returns the same type contained in the list
public <T extends Set> T something(List<T> list) {
...
}
// want to ensure both lists contain the exact same type
public <T extends Set> List<T> somethingElse(List<T> first, List<T> second) {
...
}
Simple rule: Use a type variable T extends Foo in your method signature if the same type is necessary in two places. Method parameters are each one place and the method return type is another place. Use the wildcard ? extends Foo if you just need to ensure you're dealing with "something that is a Foo" in one place.
Aside: don't use the raw type Set.
You use List<? extends Set> when you declare a varlable. For example:
List<? extends Number> l = new ArrayList<Integer>();
List<T extends Number> can be used in class or methode declaration. This will allow you to write T instead of <? extends Number> later on in the function.
public <T extends Number> int addAll(List<T> list) {
int result = 0;
for (T t : list) {
result += t.intValue();
}
return result;
}
We use wildcards to specify that the type element matches anything. The ? stands for unknown type.
List<? extends Set> is an example of a bounded wildcard and that states that the list can accept any subtype of a Set (e.g. HashSet)
List<T extends Set>, on the other hand is, allows T to be bounded to a type that extends Set.
I use wildcards when I need a collection of data irrespective pf it's exact type.
A wildcard type G<? extends A> is a super type of any G<Ai> where Ai is a subtype of A
In another word, G<? extends A> is the union type of G<A0>, ..., G<An>.