Consider this code:
public <T> List<T> meth(List<?> type)
{
System.out.println(type); // 1
return new ArrayList<String>(); // 2
}
It does not compile at line 2, saying that List is required.
Now, if it's changed to:
public <T> List<?> meth(List<T> type)
{
System.out.println(type); // 1
return new ArrayList<String>(); // 2
}
It does compile. Why? I thought the difference between declaring a generic type with T and using the wildcard was that, when using the wildcard, one cannot add new elements to a collection. Why would <?> allow a subtype of List to be returned? I'm missing something here, what's the explicit rule and how it's being applied?
The difference is in the return type declaration. List<String> is not a subtype of List<T>, but it is a subtype of List<?>.
List<?> makes no assumptions regarding its type variable, so the following statements are valid:
List<?> l0 = new ArrayList<String>();
List<?> l1 = new ArrayList<Object>();
List<? extends Number> ltemp = null;
List<?> l2 = ltemp;
List<T> assumes that the type argument will be resolved in the context of client (e.g. type use), when you declared it as List<String> or List<Object>. Within the method body, you cannot make any assumptions about it either.
In the first case, T is not necessarily a superclass of String. If you choose a T like Integer and call the method, it'll fail; so it won't compile. However, the second will compile as surely, any ArrayList<String> is a valid List of something.
As said earlier, String isn't a subtype of T, so it's why it does not work. However, this code works :
public <T> List<T> meth(List<?> type)
{
System.out.println(type); // 1
return new ArrayList<T>(); // 2
}
and is more in the idea of what you want, I think.
Related
I would like to create a generic method with a return type that contains a generic element sent in to the method.
This works fine as long as the input argument is of exact same type as the generic type. However I would like it to be possible to send an extension of the generic type.
Basically, I would like to create a method, createList, that would work for both of these calls:
List<Object> list = createList("foo");
List<String> list2 = createList("bar");
This generic method below works fine for the second call, but not for the first one.
private static <T> List<T> createList(T element) {
List<T> list = new ArrayList<>();
list.add(element);
return list;
}
For the second call I get a compilation error saying "Incompatible types. Required Object. Found String".
Below is the non-generic version which works fine for the first call, but not for the second:
private static List<Object> createList(Object element) {
List<Object> list = new ArrayList<>();
list.add(element);
return list;
}
Same compilation error here as on the other version but this time String is required, and Object was found.
Is it possible to create a method (preferrably using generics) in a way that both these calls would work? (using Java 7)
There were some type inference changes in Java 8 that fix this problem. So the straightforward solution is to update.
If you can't or don't want to update to Java 8, you can also provide the generic type explicitly:
List<Object> list = EnclosingClass.<Object>createList("foo");
Where EnclosingClass is the class that declares createList.
I'm doing something similiar, and solved the problem using a method like this.
Also this is not limited to any certain "Type" of Elements. It allows Every Object and/or primitive. IT also gives you a one-liner to fill a created list with some elements.
#SafeVarargs
private static <T extends Collection<S>, S> T makeList(T collection, S... objects) {
Collections.addAll(collection, objects);
return collection;
}
//example:
List<String> list1 = makeList(new LinkedList<String>(), "foo", "bar");
List<Integer> list2 = makeList(new ArrayList<Integer>(), 1,2,3,4);
I've read a few topics which cover certain questions about generics, such as their relationship with raw types. But I'd like an additional explanation on a certain line found in the Java SE tutorial on unbound generics .
According to a sentence :
The goal of printList is to print a list of any type, but it fails to achieve that goal — it prints only a list of Object instances; it cannot print List<Integer>, List<String>, List<Double>, and so on, because they are not subtypes of List<Object>.
If I understand well this sentence; the difference between List<?> and List<Object>, is that we can use the type argument List<String> or List<Integer> by implementing the former. While if we implement the later, we can only use the type argument List<Object>. As if List<?> is an upper bound to Object namely List<? extends Object>.
But then the following sentence confuses me, in the sense that according to what I previously understood, List<Object> should only contain instances of the class Object and not something else.
It's important to note that List<Object> and List<?> are not the same. You can insert an Object, or any subtype of Object, into a List<Object>. But you can only insert null into a List<?>.
There are two separate issues here. A List<Object> can in fact take any object as you say. A List<Number> can take at least Number objects, or of course any subclasses, like Integer.
However a method like this:
public void print(List<Number> list);
will actually only take a List which is exactly List<Number>. It will not take any list which is declared List<Integer>.
So the difference is List<?> will take any List with whatever declaration, but List<Object> will only take something that was declared as List<Object>, nothing else.
The last quote simply states, that List<?> is a list for which you literally don't know what type its items are. Because of that, you can not add anything to it other than null.
The sentence that is confusing you is trying to warn you that, while List<?> is the super-type of all generic lists, you cannot add anything to a List<?> collection.
Suppose you tried the following code:
private static void addObjectToList1(final List<?> aList, final Object o ) {
aList.add(o);
}
private static void addObjectToList2(final List<Object> aList, final Object o ) {
aList.add(o);
}
private static <T> void addObjectToList3(final List<T> aList, final T o ) {
aList.add(o);
}
public static void main(String[] args) {
List<String> testList = new ArrayList<String>();
String s = "Add me!";
addObjectToList1(testList, s);
addObjectToList2(testList, s);
addObjectToList3(testList, s);
}
addObjectToList1 doesn't compile, because you cannot add anything except null to a List<?>. (That's what the sentence is trying to tell you.)
addObjectToList2 compiles, but the call to it in main() doesn't compile, because List<Object> is not a super type of List<String>.
addObjectToList3 both compiles and the call works. This is the way to add elements to a generic list.
I am reading book for OCP of Author Jeanne Boyarsky and Scott Selikoff, Book saying : Page # 122
? super String
With a lower bound, we are telling Java that the list will be a list of String objects or a
list of some objects that are a superclass of String
List<? super String> superString = new ArrayList<>();
superString.add(new String());// Valid
superString.add(new Object()); // Not Valid ? Why?. Object is a super class of String
Other Example:
List<? super IOException> exceptions = new ArrayList<Exception>();
exceptions.add(new Exception()); // Invalid, why ? Exception is a superclass of IOException
exceptions.add(new IOException());
exceptions.add(new FileNotFoundException());
I think this statement
With a lower bound, we are telling Java that the list will be a list of String objects or a
list of some objects that are a superclass of String
Should be
With a lower bound, we are telling Java that the list will be a list of String objects or a
list of some objects whoes super class is String
if this case is true then why we use
List<? super IOException> exceptions = new ArrayList<Exception>();
instead of
List<? super IOException> exceptions = new ArrayList<IOException>();
You need to understand, that these bounds (lower and upper) exist for restricting/specifying the variable's type, and not for restricting/specifying the type of elements inside such a collection.
An example:
List<? super String> variable = new ArrayList<CharSequence>();
With this statement, you first create an ArrayList whose elements can be of type CharSequence or a subtype of it (e.g. String or StringBuilder).
With that declaration it should be clear that you can not add an Object into this list. It simply does not implement the CharSequence interface. The compiler takes care of that.
Lower and upper bounds exist for making subtypes of generic types. Their use is explained in this question about PECS.
In fact, a List<? super String> is a list with a concrete element's type, but this concrete type is not known at this moment. See my example initialisation. The concrete type is the CharSequence. It simply means that all elements are of that concrete type (or a subtype), but that is not known in the variable's own type.
Here's a link to a good explanation Difference between <? super T> and <? extends T> in Java
The book is correct "? super String" means that the list may contain Strings or a supertype of String (eg. Object, CharSequence)
In you example:
List<? super String> superString = new ArrayList<>();
superString.add(new String());// 1
superString.add(new Object()); // 2
Write operations:
1 - is valid for any type of list you could've created , since String is an Object, a CharSequence...
List<? super String> list = new ArrayList<Object>();
list.add("String");
2 - not valid because it doesn't cover all cases
List<? super String> list = new ArrayList<String>();
list.add(new Object());
Given the declared type List<? super String> you can only add Strings (and String subtypes) , anything less than a String (supertypes) may not correspond to the actual element type.
Example:
Here's an example :
interface MyInterface {
}
class MyInterfaceImpl implements MyInterface {
}
class MyInterfaceImplExtra extends MyInterfaceImpl {
}
You may have the following situations:
List<? super MyInterfaceImpl> myList = new ArrayList<MyInterfaceImpl>();
Doesn't compile because myList variable may point to a list of either MyInterfaceImpl or MyInterface or Object. When adding to the list it's unclear exactly what kind of list you actually have, thus you are allowed only values that are applicable for all cases.
myList.add(new MyInterface(){}); // - compile error "not applicable for the arguments"
myList.add(new MyInterfaceImpl()); // - works fine
myList.add(new MyInterfaceImplExtra()); // - works fine
An example where you get a list of values. The element type is Object , a more concrete type can't be guaranteed. For example here you expect Strings but get a List<? super String> that actually contains Objects so you get a java.lang.ClassCastException
List<String> result = (List<String>) getList();
System.out.println(result.get(0));
public static List<? super String> getList(){
List<Object> list = new ArrayList<Object>();
list.add(new Object());
return list;
}
Usage example in Java 8 :
You have the following method declaration in the Stream interface that restricts the predicate type to be T or supertype.
Stream<T> filter(Predicate<? super T> predicate);
This ensures that given a class Person that defines name and Employee that extends Person and defines an extra field id , you can't do something like this:
List<Person> list = new ArrayList<Person>() {{
add(new Person("John"));
add(new Person("Max"));
add(new Person("Megan"));
}};
list.stream().filter((Employee e) -> e.getId().startsWith("1")); // compile error
Let's first consider a simple scenario (see complete source on ideone.com):
import java.util.*;
public class TwoListsOfUnknowns {
static void doNothing(List<?> list1, List<?> list2) { }
public static void main(String[] args) {
List<String> list1 = null;
List<Integer> list2 = null;
doNothing(list1, list2); // compiles fine!
}
}
The two wildcards are unrelated, which is why you can call doNothing with a List<String> and a List<Integer>. In other words, the two ? can refer to entirely different types. Hence the following does not compile, which is to be expected (also on ideone.com):
import java.util.*;
public class TwoListsOfUnknowns2 {
static void doSomethingIllegal(List<?> list1, List<?> list2) {
list1.addAll(list2); // DOES NOT COMPILE!!!
// The method addAll(Collection<? extends capture#1-of ?>)
// in the type List<capture#1-of ?> is not applicable for
// the arguments (List<capture#2-of ?>)
}
}
So far so good, but here's where things start to get very confusing (as seen on ideone.com):
import java.util.*;
public class LOLUnknowns1 {
static void probablyIllegal(List<List<?>> lol, List<?> list) {
lol.add(list); // this compiles!! how come???
}
}
The above code compiles for me in Eclipse and on sun-jdk-1.6.0.17 in ideone.com, but should it? Is it not possible that we have a List<List<Integer>> lol and a List<String> list, the analogous two unrelated wildcards situations from TwoListsOfUnknowns?
In fact the following slight modification towards that direction does not compile, which is to be expected (as seen on ideone.com):
import java.util.*;
public class LOLUnknowns2 {
static void rightfullyIllegal(
List<List<? extends Number>> lol, List<?> list) {
lol.add(list); // DOES NOT COMPILE! As expected!!!
// The method add(List<? extends Number>) in the type
// List<List<? extends Number>> is not applicable for
// the arguments (List<capture#1-of ?>)
}
}
So it looks like the compiler is doing its job, but then we get this (as seen on ideone.com):
import java.util.*;
public class LOLUnknowns3 {
static void probablyIllegalAgain(
List<List<? extends Number>> lol, List<? extends Number> list) {
lol.add(list); // compiles fine!!! how come???
}
}
Again, we may have e.g. a List<List<Integer>> lol and a List<Float> list, so this shouldn't compile, right?
In fact, let's go back to the simpler LOLUnknowns1 (two unbounded wildcards) and try to see if we can in fact invoke probablyIllegal in any way. Let's try the "easy" case first and choose the same type for the two wildcards (as seen on ideone.com):
import java.util.*;
public class LOLUnknowns1a {
static void probablyIllegal(List<List<?>> lol, List<?> list) {
lol.add(list); // this compiles!! how come???
}
public static void main(String[] args) {
List<List<String>> lol = null;
List<String> list = null;
probablyIllegal(lol, list); // DOES NOT COMPILE!!
// The method probablyIllegal(List<List<?>>, List<?>)
// in the type LOLUnknowns1a is not applicable for the
// arguments (List<List<String>>, List<String>)
}
}
This makes no sense! Here we aren't even trying to use two different types, and it doesn't compile! Making it a List<List<Integer>> lol and List<String> list also gives a similar compilation error! In fact, from my experimentation, the only way that the code compiles is if the first argument is an explicit null type (as seen on ideone.com):
import java.util.*;
public class LOLUnknowns1b {
static void probablyIllegal(List<List<?>> lol, List<?> list) {
lol.add(list); // this compiles!! how come???
}
public static void main(String[] args) {
List<String> list = null;
probablyIllegal(null, list); // compiles fine!
// throws NullPointerException at run-time
}
}
So the questions are, with regards to LOLUnknowns1, LOLUnknowns1a and LOLUnknowns1b:
What types of arguments does probablyIllegal accept?
Should lol.add(list); compile at all? Is it typesafe?
Is this a compiler bug or am I misunderstanding the capture conversion rules for wildcards?
Appendix A: Double LOL?
In case anyone is curious, this compiles fine (as seen on ideone.com):
import java.util.*;
public class DoubleLOL {
static void omg2xLOL(List<List<?>> lol1, List<List<?>> lol2) {
// compiles just fine!!!
lol1.addAll(lol2);
lol2.addAll(lol1);
}
}
Appendix B: Nested wildcards -- what do they really mean???
Further investigation indicates that perhaps multiple wildcards has nothing to do with the problem, but rather a nested wildcard is the source of the confusion.
import java.util.*;
public class IntoTheWild {
public static void main(String[] args) {
List<?> list = new ArrayList<String>(); // compiles fine!
List<List<?>> lol = new ArrayList<List<String>>(); // DOES NOT COMPILE!!!
// Type mismatch: cannot convert from
// ArrayList<List<String>> to List<List<?>>
}
}
So it looks perhaps a List<List<String>> is not a List<List<?>>. In fact, while any List<E> is a List<?>, it doesn't look like any List<List<E>> is a List<List<?>> (as seen on ideone.com):
import java.util.*;
public class IntoTheWild2 {
static <E> List<?> makeItWild(List<E> list) {
return list; // compiles fine!
}
static <E> List<List<?>> makeItWildLOL(List<List<E>> lol) {
return lol; // DOES NOT COMPILE!!!
// Type mismatch: cannot convert from
// List<List<E>> to List<List<?>>
}
}
A new question arises, then: just what is a List<List<?>>?
As Appendix B indicates, this has nothing to do with multiple wildcards, but rather, misunderstanding what List<List<?>> really means.
Let's first remind ourselves what it means that Java generics is invariant:
An Integer is a Number
A List<Integer> is NOT a List<Number>
A List<Integer> IS a List<? extends Number>
We now simply apply the same argument to our nested list situation (see appendix for more details):
A List<String> is (captureable by) a List<?>
A List<List<String>> is NOT (captureable by) a List<List<?>>
A List<List<String>> IS (captureable by) a List<? extends List<?>>
With this understanding, all of the snippets in the question can be explained. The confusion arises in (falsely) believing that a type like List<List<?>> can capture types like List<List<String>>, List<List<Integer>>, etc. This is NOT true.
That is, a List<List<?>>:
is NOT a list whose elements are lists of some one unknown type.
... that would be a List<? extends List<?>>
Instead, it's a list whose elements are lists of ANY type.
Snippets
Here's a snippet to illustrate the above points:
List<List<?>> lolAny = new ArrayList<List<?>>();
lolAny.add(new ArrayList<Integer>());
lolAny.add(new ArrayList<String>());
// lolAny = new ArrayList<List<String>>(); // DOES NOT COMPILE!!
List<? extends List<?>> lolSome;
lolSome = new ArrayList<List<String>>();
lolSome = new ArrayList<List<Integer>>();
More snippets
Here's yet another example with bounded nested wildcard:
List<List<? extends Number>> lolAnyNum = new ArrayList<List<? extends Number>>();
lolAnyNum.add(new ArrayList<Integer>());
lolAnyNum.add(new ArrayList<Float>());
// lolAnyNum.add(new ArrayList<String>()); // DOES NOT COMPILE!!
// lolAnyNum = new ArrayList<List<Integer>>(); // DOES NOT COMPILE!!
List<? extends List<? extends Number>> lolSomeNum;
lolSomeNum = new ArrayList<List<Integer>>();
lolSomeNum = new ArrayList<List<Float>>();
// lolSomeNum = new ArrayList<List<String>>(); // DOES NOT COMPILE!!
Back to the question
To go back to the snippets in the question, the following behaves as expected (as seen on ideone.com):
public class LOLUnknowns1d {
static void nowDefinitelyIllegal(List<? extends List<?>> lol, List<?> list) {
lol.add(list); // DOES NOT COMPILE!!!
// The method add(capture#1-of ? extends List<?>) in the
// type List<capture#1-of ? extends List<?>> is not
// applicable for the arguments (List<capture#3-of ?>)
}
public static void main(String[] args) {
List<Object> list = null;
List<List<String>> lolString = null;
List<List<Integer>> lolInteger = null;
// these casts are valid
nowDefinitelyIllegal(lolString, list);
nowDefinitelyIllegal(lolInteger, list);
}
}
lol.add(list); is illegal because we may have a List<List<String>> lol and a List<Object> list. In fact, if we comment out the offending statement, the code compiles and that's exactly what we have with the first invocation in main.
All of the probablyIllegal methods in the question, aren't illegal. They are all perfectly legal and typesafe. There is absolutely no bug in the compiler. It is doing exactly what it's supposed to do.
References
Angelika Langer's Java Generics FAQ
Which super-subtype relationships exist among instantiations of generic types?
Can I create an object whose type is a wildcard parameterized type?
JLS 5.1.10 Capture Conversion
Related questions
Any simple way to explain why I cannot do List<Animal> animals = new ArrayList<Dog>()?
Java nested wildcard generic won’t compile
Appendix: The rules of capture conversion
(This was brought up in the first revision of the answer; it's a worthy supplement to the type invariant argument.)
5.1.10 Capture Conversion
Let G name a generic type declaration with n formal type parameters A1…An with corresponding bounds U1…Un. There exists a capture conversion from G<T1…Tn> to G<S1…Sn>, where, for 1 <= i <= n:
If Ti is a wildcard type argument of the form ? then …
If Ti is a wildcard type argument of the form ? extends Bi, then …
If Ti is a wildcard type argument of the form ? super Bi, then …
Otherwise, Si = Ti.
Capture conversion is not applied recursively.
This section can be confusing, especially with regards to the non-recursive application of the capture conversion (hereby CC), but the key is that not all ? can CC; it depends on where it appears. There is no recursive application in rule 4, but when rules 2 or 3 applies, then the respective Bi may itself be the result of a CC.
Let's work through a few simple examples:
List<?> can CC List<String>
The ? can CC by rule 1
List<? extends Number> can CC List<Integer>
The ? can CC by rule 2
In applying rule 2, Bi is simply Number
List<? extends Number> can NOT CC List<String>
The ? can CC by rule 2, but compile time error occurs due to incompatible types
Now let's try some nesting:
List<List<?>> can NOT CC List<List<String>>
Rule 4 applies, and CC is not recursive, so the ? can NOT CC
List<? extends List<?>> can CC List<List<String>>
The first ? can CC by rule 2
In applying rule 2, Bi is now a List<?>, which can CC List<String>
Both ? can CC
List<? extends List<? extends Number>> can CC List<List<Integer>>
The first ? can CC by rule 2
In applying rule 2, Bi is now a List<? extends Number>, which can CC List<Integer>
Both ? can CC
List<? extends List<? extends Number>> can NOT CC List<List<Integer>>
The first ? can CC by rule 2
In applying rule 2, Bi is now a List<? extends Number>, which can CC, but gives a compile time error when applied to List<Integer>
Both ? can CC
To further illustrate why some ? can CC and others can't, consider the following rule: you can NOT directly instantiate a wildcard type. That is, the following gives a compile time error:
// WildSnippet1
new HashMap<?,?>(); // DOES NOT COMPILE!!!
new HashMap<List<?>, ?>(); // DOES NOT COMPILE!!!
new HashMap<?, Set<?>>(); // DOES NOT COMPILE!!!
However, the following compiles just fine:
// WildSnippet2
new HashMap<List<?>,Set<?>>(); // compiles fine!
new HashMap<Map<?,?>, Map<?,Map<?,?>>>(); // compiles fine!
The reason WildSnippet2 compiles is because, as explained above, none of the ? can CC. In WildSnippet1, either the K or the V (or both) of the HashMap<K,V> can CC, which makes the direct instantiation through new illegal.
No argument with generics should be accepted. In the case of LOLUnknowns1b the null is accepted as if the first argument was typed as List. For example this does compile :
List lol = null;
List<String> list = null;
probablyIllegal(lol, list);
IMHO lol.add(list); shouldn't even compile but as lol.add() needs an argument of type List<?> and as list fits in List<?> it works.
A strange example which make me think of this theory is :
static void probablyIllegalAgain(List<List<? extends Number>> lol, List<? extends Integer> list) {
lol.add(list); // compiles fine!!! how come???
}
lol.add() needs an argument of type List<? extends Number> and list is typed as List<? extends Integer>, it fits in. It won't work if it doesn't match.
Same thing for the double LOL, and other nested wildcards, as long as the first capture matches the second one, everything is okay (and souldn't be).
Again, I'm not sure but it does really seem like a bug.
I'm glad to not be the only one to use lol variables all the time.
Resources :
http://www.angelikalanger.com, a FAQ about generics
EDITs :
Added comment about the Double Lol
And nested wildcards.
not an expert, but I think I can understand it.
let's change your example to something equivalent, but with more distinguishing types:
static void probablyIllegal(List<Class<?>> x, Class<?> y) {
x.add(y); // this compiles!! how come???
}
let's change List to [] to be more illuminating:
static void probablyIllegal(Class<?>[] x, Class<?> y) {
x.add(y); // this compiles!! how come???
}
now, x is not an array of some type of class. it is an array of any type of class. it can contain a Class<String> and a Class<Int>. this cannot be expressed with ordinary type parameter:
static<T> void probablyIllegal(Class<T>[] x //homogeneous! not the same!
Class<?> is a super type of Class<T> for any T. If we think a type is a set of objects, set Class<?> is the union of all sets of Class<T> for all T. (does it include itselft? I dont know...)
The error:
The method add(capture#1-of ?) in the type List<capture#1-of ?> is not
applicable for the arguments (String)
Code:
List<?> to = new ArrayList<Object>();
to.add(new String("here"));
Since List<?> is a generic type List and therefore can be of any type, then why it is not accepting String in add method?
A List<?> is a list of some type, which is unknown. So you can't add anything to it except null without breaking the type-safety of the list:
List<Integer> intList = new ArrayList<>();
List<?> unknownTypeList = intList;
unknownTypeList.add("hello"); // doesn't compile, now you should see why
should a String not be acceptable ?
No. <?> means the type is unknown and the compiler cannot be sure that any type is acceptable to add (this include String)
You can specify a lower bound:
List<? super Object> to = new ArrayList<Object>();
to.add(new String("here")); // This compiles
Now the compiler is sure that the list can contain any Object
According to wildcards, the question mark (?), called the wildcard, represents an unknown type and not a generic type, since its an unknown type the compiler cannot accept String in your case.
You can consider any list defined using wildcards to be read only. Still, there are some non-read operations that you can do.
From the docs:
You can add null.
You can invoke clear.
You can get the iterator and invoke remove.
You can capture the wildcard and write elements that you've read from the list.
List<?> could be rewritten as List<? extends Object> and ? extends Object means anything is subclass of Object.
For clarity, assuming we only have two subclass of Object here like String and Integer.
List<? extends Object> means this list can store a list of String such as List<String> or a list of Integer such as List<Integer>, but not a list of mixed String and Integer.
Back to your example, the list to can be a list of String or a list of Integer. When you try to add String into the list, it means this list can only store a list of String. However, the complier know that this list can also be a list of Integer and complier doesn't know which one is the case and an error is showed.
List<?> to = new ArrayList<Object>();
to.add(new String("here"));