I was looking at the Java Generics documentation and found this piece of code,
public class WildcardError {
void foo(List<?> l) {
//This give a compile time error
l.set(0,l.get(0));
}
}
I can understand that we are fetching an element from a List<?> and trying to set it to another List<?>. So the compiler gives an error. My question is it makes sense when the 2 lists are different i.e. l.set(0, m.get(0)) here lists l and m are different. But in the above example, l and l are the same lists. Why isn't the compiler smart enough to see that? Is it hard to implement it?
Edit:
I am aware that I can fix it by a helper method or by using T instead of a ?. Just wondering why compiler doesn't do it for me.
In your specific case, you can explicitly fix this:
public class WildcardError {
<T> void foo(List<T> l) {
// This will work
l.set(0, l.get(0));
}
}
Or if you don't want to change the original API, introduce a delegate helper method:
public class WildcardError {
void foo(List<?> l) {
foo0(l);
}
private <T> void foo0(List<T> l) {
// This will work
l.set(0, l.get(0));
}
}
Unfortunately, the compiler cannot infer that "obvious" <T> type. I've been wondering about that, too. It seems like something that could be improved in a compiler, as every wild card can be informally translated to an unknown <T> type. Probably, there are some reasons why this was omitted, perhaps this is only intuition, but formally impossible.
UPDATE:
Note, I've just seen this peculiar implementation of Collections.swap():
public static void swap(List<?> list, int i, int j) {
final List l = list;
l.set(i, l.set(j, l.get(i)));
}
The JDK guys resort to a raw type, in order to handle this, locally. This is a strong statement indicating that this probably should be supported by the compiler, but for some reason (e.g. lack of time to formally specify this) just wasn't done
The compiler reports an error because there is no way -- in general -- that it can tell whether two expressions, (in this case l and l) refer to the same list.
Related, somewhat generalized, question:
How does the JLS specify that wildcards cannot be formally used within methods?
List<?> means list containing elements of some unknown type, so when one wants to take elements from it using list.get(i) it will return object of some unknown type, so the only valid guess will be Object. Then when one tries to set element back using list.set(index, list.get(index)) it produces compile-time error, since as mentioned above List<?> can only contain some unknown type, so putting Object to it may cause ClassCastException.
This is explained very well in Joshua Bloch's Effective Java, 2nd ed., Item 28: Use bounded wildcards to increase API flexibility
This is also known as PECS principle and good explanation can be found in this Q/A:
What is PECS (Producer Extends Consumer Super)? (please note that List<?> is the same as List<? extends Object> with minor exceptions)
In laymans terms, one should use List<?> as method parameter only to get elements from it inside that method, not when one needs to put elements into list. When one needs to both put and get he/she needs to either generify method using type parameter T as in Lukas Eder's answer (type-safe way) or simply use List<Object> (not type-safe way).
Related
I want to define a function which can convert a kind of Set to another, like convert HashSet to LinkedHashSet. Here is the function declaration. The sp is sharedpreferences.
public Set<String> decodeStringSet(String key, #Nullable Set<String> defaultValue, Class<? extends Set> cls){
Set<String> result = sp.getStringSet(key, defaultValue);
if(result == null){
return defaultValue;
}
else {
String[] array = result.toArray(new String[0]);
Set<String> a;
try {
a = cls.newInstance();
} catch (IllegalAccessException | InstantiationException var7) {
return defaultValue;
}
a.addAll(Arrays.asList(array));
return a;
}
}
However, the compiler remind me that "Unchecked assignment: '? extends java.util.Set' to 'java.util.Set<java.lang.String>'" on "a = cls.newInstance();". I don't know how to change cls to cls<java.lang.String>.
The warning is unavoidable. Isolate it in a helper method and toss the appropriate #SuppressWarnings at it. Or, refactor how this thing works. In general, the generics of Class<?> are weird and don't work well; if you try to write code that relies on the generics part to make it work, it's likely to result in many situations where you can't avoid these warnings, and the API is suboptimal.1
One tricky way to do what you're trying to do here in a one-size-fits-all way is so-called Super Type Tokens. You can search the web for this concept, because for what you're specifically doing here, STTs are overkill. What you are looking for, is a supplier.
You want the caller not to pass you the type of a set. No. You want the caller to pass you a piece of code that, if executed, creates the set.
While we're at it, let's get rid of the array, you're shifting the elements through that array for absolutely no sensible reason.
public <S extends Set<String>> S decodeStringSet(String key, #Nullable Set<String> defaultValue, Supplier<S> setMaker) {
Set<String> result = sp.getStringSet(key, defaultValue);
if(result == null) return defaultValue;
S a = setMaker.get();
a.addAll(result);
return a;
}
This code can be used as follows:
LinkedHashSet<String> mySet = decodeStringSet("myKey", null, LinkedHashSet::new);
Perhaps you're unfamiliar with this syntax. new LinkedHashSet() will, when you run that code, create a LinkedHashSet. In contrast, LinkedHashSet::new will, when you run that code, produce an object that can be asked to create a LinkedHashSet, by invoking its get() method. One does the act right this very moment. The other wraps 'do the act' into a little machine. You can hand the machine to other code, or press the button on the machine to make it do the act, and you can press the button as often as you feel like.
[1] Need some more explanations as to why relying on the generics of j.l.Class is awkward and not a good idea?
A class object simply cannot, itself, represent generics, whereas generics can represent generics. That is: List<List<String>> is perfectly fine. However, Class<List<String>> does not make sense. You can write it, (j.l.Class does not have hardcoded rules to keep sanity alive in the langspec), but it doesn't represent anything: There's just one class object that represents the type j.u.List. This one object cannot therefore represent the generics; you can't have one class object representing List<String> and another representing List<Integer>. Less important, but still annoying - there are things class objects can represent that generics cannot. int.class is types as Class<Integer> but this isn't quite right.
Hence, in your example, the compiler consider Class<? extends Set> as problematic; it's got a raw type inside the generics. However, it is technically correct, in that it is not possible to represent e.g. a Set<T>, merely 'a Set, whose generics are unknown, given that j.l.Class objects cannot represent them'.
Lastly, classes basically only produce (the P in PECS - which explains what the difference is between <Number>, <? extends Number>, and <? super Number>); it is mentally difficult to fathom the difference between Class<? extends String> and Class<String>, because it's an irrelevant difference, given that j.l.Class only produces. And yet, often you really do need to write Class<? extends String> because if you don't, the compiler refuses to compile your code for imaginary, irrelevant reasons. That's because, again, j.l.Class is not hardcoded in the lang spec: The compiler does not know that there is no effective distinction between Class<T> and Class<? extends T>, and java does not have a way to mark off a given generics param as forced Produces-only or some such.
I am refreshing my knowledge on Java generics. So I turned to the excellent tutorial from Oracle ... and started to put together a presentation for my coworkers. I came across the section on wildcards in the tutorial that says:
Consider the following method, printList:
public static void printList(List<Object> list) {
...
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>. To write a generic printList method, use List<?>:
public static void printList(List<?> list) {
I understand that List<Object> will not work; but I changed the code to
static <E> void printObjects(List<E> list) {
for (E e : list) {
System.out.println(e.toString());
}
}
...
List<Object> objects = Arrays.<Object>asList("1", "two");
printObjects(objects);
List<Integer> integers = Arrays.asList(3, 4);
printObjects(integers);
And guess what; using List<E> I can print different types of Lists without any problem.
Long story short: at least the tutorial indicates that one needs the wildcard to solve this problem; but as shown, it can be solved this way too. So, what am I missing?!
(side note: tested with Java7; so maybe this was a problem with Java5, Java6; but on the other hand, Oracle seems to do a good job regarding updates of their tutorials)
Your approach of using a generic method is strictly more powerful than a version with wildcards, so yes, your approach is possible, too. However, the tutorial does not state that using a wildcard is the only possible solution, so the tutorial is also correct.
What you gain with the wildcard in comparison to the generic method: You have to write less and the interface is "cleaner" since a non generic method is easier to grasp.
Why the generic method is more powerful than the wildcard method: You give the parameter a name which you can reference. For example, consider a method that removes the first element of a list and adds it to the back of the list. With generic parameters, we can do the following:
static <T> boolean rotateOneElement(List<T> l){
return l.add(l.remove(0));
}
with a wildcard, this is not possible since l.remove(0) would return capture-1-of-?, but l.add would require capture-2-of-?. I.e., the compiler is not able to deduce that the result of remove is the same type that add expects. This is contrary to the first example where the compiler can deduce that both is the same type T. This code would not compile:
static boolean rotateOneElement(List<?> l){
return l.add(l.remove(0)); //ERROR!
}
So, what can you do if you want to have a rotateOneElement method with a wildcard, since it is easier to use than the generic solution? The answer is simple: Let the wildcard method call the generic one, then it works:
// Private implementation
private static <T> boolean rotateOneElementImpl(List<T> l){
return l.add(l.remove(0));
}
//Public interface
static void rotateOneElement(List<?> l){
rotateOneElementImpl(l);
}
The standard library uses this trick in a number of places. One of them is, IIRC, Collections.java
Technically, there is no difference between
<E> void printObjects(List<E> list) {
and
void printList(List<?> list) {
When you are declaring a type parameter, and using it only once, it essentially becomes a wildcard parameter.
On the other hand, if you use it more than once, the difference becomes significant. e.g.
<E> void printObjectsExceptOne(List<E> list, E object) {
is completely different than
void printObjects(List<?> list, Object object) {
You might see that first case enforces both types to be same. While there is no restriction in second case.
As a result, if you are going to use a type parameter only once, it does not even make sense to name it. That is why java architects invented so called wildcard arguments (most probably).
Wildcard parameters avoid unnecessary code bloat and make code more readable. If you need two, you have to fall back to regular syntax for type parameters.
Hope this helps.
Both solutions are effectively the same, it's just that in the second one you are naming the wildcard. This can come handy when you want to use the wildcard several times in the signature, but want to make sure that both refer to the same type:
static <E> void printObjects(List<E> list, PrintFormat<E> format) {
Compiling the below code is failing:
public static void swap(List<?> list, int i, int j) {
list.set(i, list.set(j, list.get(i)));
}
like:
Swap.java:5: set(int,capture#282 of ?) in List<capture#282 of ?> cannot be applied to (int,Object)
list.set(i, list.set(j, list.get(i)));
But if I do this:
public static void swap(List<?> list, int i, int j) {
swapHelper(list, i, j);
}
private static <E> void swapHelper(List<E> list, int i, int j) { list.set(i, list.set(j, list.get(i)));
}
Its working perfectly.
But I have a basic doubt here. Generics are said to be invariant, so List<String> is not subtype of List<Object>, right?
If that is the case, then how come in the above method, we are able to pass List<?> to List<E>? How does this works?
the answer is wildcards.
List<?> is not the same as List<Object>
while java assumes both are collections of Object, the former will match the type of any other List<T>, while the latter will not match the type of any List<T> besides List<Object>.
example:
public void doSomething(List<Object> list);
This function will accept only List<Object> as it's parameter. However this:
public void doSomething(List<?> list);
will accept any List<T> as it's parameter.
This can be extremely useful when used with generic constraints. For instance if you'd like to write a function that manipulates numbers (Integers, Floats, etc.) you could:
public void doSomethingToNumbers(List<? extends Number> numbers) { ... }
Because you are using wildcard capturing.
In some cases, the compiler infers the type of a wildcard. For
example, a list may be defined as List but, when evaluating an
expression, the compiler infers a particular type from the code. This
scenario is known as wildcard capture.
Thanks to the helper method, the compiler uses inference to determine T, the capture variable, in the invocation.
List<?> means "list of an unknown type". You can pass a List<String> for it, not because it is a subclass (it isn't), but because "unknown type" is supposed to accept whatever you throw at it.
Because type is unknown, you cannot perform operations with it, that require knowing the type of the elements (like set or add etc).
List<E> is a generic. It is not the same as List<?> even though it kinda looks similar.
One difference is, as you noted, you can do things like set or add to it, because the type of the element is now known.
Whildcards as function parameters are not very useful (<E> void foo(List<E> l) is not much different from void foo(List<?> l)). They also bypass all the compile-time type check, thus defeating the purpose of generics, and should really be avoided.
A more common (and less harmful) use for wildcards is in the return types: List<? extends DataIterface> getData() means that getData returns a list of some objects that implement a DataInterface, but won't tell you their concrete type. This is often done in the API design to isolate implementation details from the interface. The convention usually is, that you can pass a list of objects, returned by the API to other API methods, and they will accept and handle them. This concept is called existential types.
Also, note that getData from the above example can return lists of different types, depending on some condition, that is outside of the caller's domain, unlike if it was declared to return List<E>, in which case, the caller would have to specify the expected type in some way.
Reading the Java online tutorial I haven't understood anything about wildcard capture.
For example:
import java.util.List;
public class WildcardError {
void foo(List<?> i) {
i.set(0, i.get(0));
}
}
Why can't the compiler retain the assignment safely?
It knows that, by executing for instance, the method with an Integer List, it gets from i.get an Integer value. So it tries to set an Integer value at index 0 to the same Integer list (i).
So, what's wrong? Why write Wildcard helper?
why the compiler can't retain the assignment safe? It knows that,by executing for instance, the method with an Integer List, it gets from i.get an Integer value. So it try to set an Integer value at index 0 to the same Integer list (i).
Put differently, why does the compiler not know that the two usages of the wildcard type List<?> in
i.set(0, i.get(0));
refer to the same actual type?
Well, that would require the compiler to know that i contains the same instance for both evaluations of the expression. Since i isn't even final, the compiler would have to check whether i could possibly have been assigned in between evaluating the two expressions. Such an analysis is only simple for local variables (for who knows whether an invoked method will update a particular field of a particular object?). This is quite a bit of additional complexity in the compiler for rarely manifesting benefits. I suppose that's why the designers of the Java programming language kept things simple by specifying that different uses of the same wildcard type have different captures.
why the compiler can't retain the assignment safe?
The compiler doesn't know anything about the type of elements in List<?> i, by definition of ?. Wildcard does not mean "any type;" it means "some unknown type."
It knows that,by executing for instance, the method with an Integer List, it gets from i.get an Integer value.
That's true, but as I said above: the compiler can only know – at compile time, remember – that i.get(0) returns an Object, which is the upper bound of ?. But there's no guarantee that ? is at runtime Object, so there is no way for the compiler to know that i.set(0, i.get(0)) is a safe call. It's like writing this:
List<Foo> fooz = /* init */;
Object foo = fooz.get(0);
fooz.set(0, foo); // won't compile because foo is an object, not a Foo
More reading:
Can't add value to the Java collection with wildcard generic type
Java Collections using wildcard
Generic collection & wildcard in java
Generics - Cannot add to a List with unbounded wildcard
What is the difference betwen Collection<?> and Collection<T>
According to Get-Put principle:
If you have extends wildcard as in List<? extends Something>, then:
1A. You can get from the structure using Something or its superclass reference.
void foo(List<? extends Number> nums) {
Number number = nums.get(0);
Object number = nums.get(0); // superclass reference also works.
}
1B. You cannot add anything to the structure (except null).
void foo(List<? extends Number> nums) {
nums.add(1); Compile error
nums.add(1L); Compile error
nums.add(null); // only null is allowed.
}
Similarly, if you have super wildcard as in List<? super Something>, then:
2A. You can add to the structure which is Something or its subclass.
For eg:
void foo(List<? super Number> nums) {
nums.add(1); // Integer is a subclass of Number
nums.add(1L); // Long is a subclass of Number
nums.add("str"); // Compile error: String is not subclass of Number
}
2A. You cannot get from the structure (except via Object reference).
For eg:
void foo(List<? super Integer> nums) {
Integer num = nums.get(0); // Compile error
Number num = nums.get(0); // Compile error
Object num = nums.get(0); // Only get via Object reference is allowed.
}
Coming back to OP's question, List<?> i is just the short representation for List<? extends Object> i. And since it's a extends wildcard, the set operation fails.
The final piece remaining is WHY the operation fails ? Or why the Get-Put principle in the first place? - This has to do with type safety as answered by Jon Skeet here.
I also find this question hard to understand; splitting the single command into two commands helped me.
Below code is what actually happens in the background when the original method is inspected&compiled, the compiler makes its own local variable: the result of the i.get(0) call is placed in the register on the local variable stack.
And that is - for the understanding of this issue - the same as making a local variable which for convenience I have given the name element.
import java.util.List;
public class WildcardError {
void foo(List<?> i) {
Object element = i.get(0); // command 1
i.set(0, element); // command 2
}
}
When command 1 is inspected, it can only set the type of element to Object (--> upperbound concept, see Matt's answer), as it can not use ? as a variable type; the ? is only used for indicating that the generic type is unknown.
Variable types can only be real types or generic types, but since you don't use a generic type in this method like <T> for example, it is forced to use a real type. This forcing is done because of the following lines in the java specification (jls8, 18.2.1):
A constraint formula of the form ‹Expression → T› is reduced as follows:
[...]
– If the expression is a class instance creation expression or a method invocation expression, the constraint reduces to the bound set B3 which would be used to determine the expression's invocation type when targeting T, as defined in §18.5.2. (For a class instance creation expression, the corresponding "method" used for inference is defined in §15.9.3).
Solution Will Be,
import java.util.List;
public class WildcardError {
private void fooHelper(List<T> i){
i.set(0, i.get(0));
}
public void foo(List<?> i){
fooHelper(i);
}
}
here fooHelper will capture type T of wildcard ? (so as the name wildcard capture).
I think you misunderstand the ? in Generics. It isn't "wildcard capture" at runtime. It is "wildcard capture" at compile time. Since that capture is gone after compilation, the List<?> often becomes List<Object> or a list of some interface or common base class that is the lowest subclass that is shared amongst the instances according to the Generics type.
Because Java had to make the Generics system compatible with non-Generics supporting JVMs, there are no Generics types at runtime. This means that all checks / captures don't exist at runtime. Either the compiler permits a very narrow range of compatible Generics type assignments when it compiles, or it throws an error.
Now if you want to hold a Generics type at runtime, there are approaches. Most of them involve passing the Class object as a parameter into a constructor, which holds it in a field. Then you can refer to the field for the exact passed type. You can entangle this type with your Generics parameters; and by doing so, you can then have the actual type in places in the runtime code where the "Generics type parameters" have been replaced by their most permissive type supported in the real Java type system. I don't recommend doing this for many situations, as it is a lot of work (and not likely to give you much more security); but, occasionally you really do need the type.
I guess your misunderstanding of the restriction comes from substitution of ? for any type, Object or something like that in your mind. But that assumption is not correct, ? in fact means unknown type. So in the following line
fooz.set(0, foo);
you're trying to assign the variable of some type to the variable of unknown type (since the function signature is like void set(int, ?)), which can never be possible, whatever the type of foo would be. In your case the type of foo is Object and you cannot assign it to a variable of some unknown type, which in fact may be Foo, Bar or any other.
I saw a java function that looked something like this-
public static<T> foo() {...}
I know what generics are but can someone explain the in this context? Who decides what T is equal to? Whats going on here?
EDIT: Can someone please show me an example of a function like this.
You've missed the return type out, but apart from that it's a generic method. As with generic types, T stands in for any reference type (within bounds if given).
For methods, generic parameters are typically inferred by the compiler. In certain situations you might want to specify the generic arguments yourself, using a slightly peculiar syntax:
List<String> strings = Collections.<String>emptyList();
In this case, the compiler could have inferred the type, but it's not always obvious whether the compiler can or can't. Note, the <> is after the dot. For syntactical reasons the type name or target object must always be specified.
It's possible to have generic constructors, but I've never seen one in the wild and the syntax gets worse.
I believe C++ and C# syntaxes place the generic types after the method/function name.
The context is a generic method as opposed to a class. The variable <T> applies only to the call of the method.. The Collections class has a number of these; the class itself is not generic, but many of the methods are.
The compiler decides what T is equal to -- it equals whatever gets the types to work. Sometimes this is easier then others.
For example, the method static <T> Set<T> Collections.singleton(T o) the type is defined in the parameter:
Collections.singleton(String T)
will return a Set<String>.
Sometimes the type is hard to define. For example sometimes there is not easily enough information to type Collection.emptyList(). In that case you can specify the type directly: Collection.<String>emptyList().
T it's the formal type parameter wich will be replaced by the actual type
argument used at the instantiation of the object.
For example, here is the List and Iterator definitios in package java.util:
public interface List<E>{
void add(E x);
Iterator<E> iterator();
}
public interface Iterator<E>{
E next();
boolean hasNext();
}
Then you can instantiate a List this way:
List<String> ls = new ArrayList<String>()
Where you might imagine that List stands for a version of List where E has
been uniformly replaced by String:
public interface StringList{
void add(String x)
Iterator<String> iterator();
}