Java Generics (Wildcards)
In your first question, <? extends T>
and <? super T>
are examples of bounded wildcards. An unbounded wildcard looks like <?>
, and basically means <? extends Object>
. It loosely means the generic can be any type. A bounded wildcard (<? extends T>
or <? super T>
) places a restriction on the type by saying that it either has to extend a specific type (<? extends T>
is known as an upper bound), or has to be an ancestor of a specific type (<? super T>
is known as a lower bound).
The Java Tutorials have some pretty good explanations of generics in the articles Wildcards and More Fun with Wildcards.
can we use wildcards at class level in java
You cannot.
From Wiki of WildCard
The wildcard ? in Java is a special actual parameter for the instantiation of generic (parameterized) types. It can be used for the instantiation, not in the definition of a generic unit.
Java generics : wildcard extends class and include that class
An extends
(or super
) bound always includes the bound itself.
The problem in your case is that a class literal, like AssetKey.class
, always has the type Class
parameterized by the non-parameterized form of that class. So AssetKey.class
has type Class<AssetKey>
. And Class<AssetKey>
is not a subtype of Class<? extends AssetKey<?>>
because the raw type AssetKey
is not a subtype of wildcard-parameterized type AssetKey<?>
. (According to Java rules, the raw and parameterized versions of a type are not subtypes of each other.)
On the other hand, TextureKey.class
has class Class<TextureKey>
, which is a subtype of Class<? extends AssetKey<?>>
, because the type TextureKey
(a non-generic class) is declared to be a subtype of AssetKey<Texture>
, which is a subtype of AssetKey<?>
.
If you drop the wildcard, and just declare it as Class<? extends AssetKey> assetKeyClass;
, it will work because AssetKey
is a subtype of AssetKey
, and thus Class<AssetKey>
is a subtype of Class<? extends AssetKey>
.
When to use wildcards in Java Generics?
The big difference between
public <T extends Animal> void takeThing(ArrayList<T> list)
and
public void takeThing(ArrayList<? extends Animal> list)
is that in the former method you can refer to "T" within the method as the concrete class that was given. In the second method you cannot do this.
Here a more complex example to illustrate this:
// here i can return the concrete type that was passed in
public <T extends Animal> Map<T, String> getNamesMap(ArrayList<T> list) {
Map<T, String> names = new HashMap<T, String>();
for (T animal : list) {
names.put(animal, animal.getName()); // I assume there is a getName() method
}
return names;
}
// here i have to use general Animal
public Map<Animal, String> getNamesMap(ArrayList<? extends Animal> list) {
Map<Animal, String> names = new HashMap<Animal, String>();
for (Animal animal : list) {
names.put(animal, animal.getName()); // I assume there is a getName() method
}
return names;
}
With the first method if you pass in an List of Cats you get a Map with Cat as key. The second method would always return a Map with general Animal key.
By the way this is not valid java syntax:
public <? extends Animal> void takeThing(ArrayList<?> list)
Using this form of generic method declaration you have to use a valid java identifier and not "?".
Edit:
The form "? extends Type" only applies to variable or parameter type declaration. Within a generic method declration it has to be "Identifier extends Type" as you are able to refer to the "Identifier" from within your method.
Java generics, Unbound wildcards ? vs Object
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
.
Java Generics: What is the benefit of using wildcards here?
This is a really good question and the simple answer was guessed already:
For the current version of the
fill(List<? super T> list, T obj)
there is no
such input that would be rejected given the signature is changed tofill(List<T> list, T obj)
, so there is no benefit and the devs are likely followed the PECS principle
The above statement derives from the principle that: if there is a such type X
so thatX
is a supertype of T
then List<X>
is a supertype of List<? super T>
because of type contravariance.
Since we can always find such X
(at the worst case it's the Object
class) - the compiler can infer a suitable List<X>
argument type given either form of fill
.
So, knowing that fact we can interfere with the compiler and infer the type ourselves using "type witness" so the code breaks:
List<Object> target = new ArrayList<>();
//Compiles OK as we can represent List<Object> as List<? super Integer> and it fits
Collections.<Integer>fill(target, 1);
//Compilation error as List<Object> is invariant to List<Integer> and not a valid substitute
Collections.<Integer>fillNew(target, 1);
This is all of course purely theoretical and nobody in their right mind would use the type argument there.
HOWEVER
While answering the question "What is the benefit of using wildcards here?" we yet considered only one side of the equation - us, consumers of the method and our experience but not library developers.
Hence this question is somewhat similar to why Collections.enumeration(final Collection<T> c)
is declared the way it is and not enumeration(Collection<T> c)
as final
seems superfluous for the end-user.
We can speculate here about the real intention, but I can give a few subjective reasons:
- First: using
List<? super T>
(as well asfinal
forenumeration
) immediately disambiguates the code that tiny bit more and for the<? super T>
specifically - it useful to show that only partial knowledge about the
type parameter is required and thelist
cannot be used to produce values of T, but only to consume them.
Quote:
Wildcards are useful in situations where only partial knowledge about the type parameter is required.
JLS 4.5.1. Type Arguments of Parameterized Types
- Second: it gives some freedom to the library owners to improve/update the method without breaking backward compatibility while conforming to the existing constraints.
Now let's try make up some hypothetical "improvements" to see what I mean (I'll call the form of fill
that uses List<T>
as fillNew
):
#1 The decision is to make method to return the obj
value (used to fill up the list) back:
public static <T> void fill(List<? super T> list, T obj)
//becomes ↓↓↓
public static <T> T fill(List<? super T> list, T obj)
The updated method would work just fine for fill
signature, but for fillNew
- the inferred return type now isn't that obvious:
List<Number> target = new ArrayList<>();
Long val = fill(target, 1L); //<<Here Long is the most specific type that fits both arguments
//Compilation error
Long val = fillNew(target, 1L); //<<Here Number is, so it cannot be assigned back
//More exotic case:
Integer val = fill(asList(true), 0); //val is Integer as expected
Comparable<?> val = fillNew(asList(true), 0); //val is now Comparable<?> as the most specific type
#2 The decision to add an overloaded version of fill
that is 10x more performant in cases when T
is Comparable<T>
:
/* Extremely performant 10x version */
public static <T extends Comparable<T>> void fill(List<? super T> list, T value)
/* Normal version */
public static void fill(List<? super T> list, T value)
List<Number> target = new ArrayList<>();
fill(target, 1); //<<< Here the more performant version is used as T inferred to Integer and it implements Comparable<Integer>
fillNew(target, 1); //<< Still uses the slow version just because T is inferred to Number which is not Comparable
To sum up - the current signature of fill
is more flexible/descriptive in my opinion for all parties (developers and library designers)
Related Topics
Non Resizable Window Border and Positioning
Java Says Filenotfoundexception But File Exists
When Exactly Is It Leak Safe to Use (Anonymous) Inner Classes
Change the System Brightness Programmatically
How to Convert a Part of Java Source File to Kotlin
How to Find a Java Thread Running on Linux with Ps -Axl
How to Set Ld_Library_Path for Java Process
How to Set Classpath in Linux to Let Java Find Jar File
Eclipse Swt Browser Crash (Linux 64Bit)
How to Instantiate an Abstract Class
Replace String With Another in Java
How to Add a Textview to Linearlayout in Android
Firebase:Recycler View No Adater Attached , Skipping Layout
Eclipse Release Heap Back to System
Hadoop Mapper Is Failing Because of "Container Killed by the Applicationmaster"