-
Notifications
You must be signed in to change notification settings - Fork 48
Generic Type Parameter and Generic Wildcard
- T, E, K, and V are all Generic Type Parameters, there is no difference, just a symbol meaning by convention.
- Defined by capital letters A, B, C, D, ... X, Y, Z are all Generic Type Parameters, T and A are the same, T is just the meaning of the name.
Symbol | Description |
---|---|
T | A specific java type |
K, V | Key, Value in the java |
E | Element |
public class Generic<T> {
private final T key;
public Generic(T key) {
this.key = key;
}
}
and
public class Generic<A> {
private final A key;
public Generic(A key) {
this.key = key;
}
}
Replacing T with A makes no difference in the execution effect, but T stands for type by convention, so it is better to follow the convention, which increases the readability of the code.
If need to define multiple Generic Type Parameters, such as key (K) and value (V) Generic Type Parameters in Map:
public interface MyMap<K, V> {
public K getKey();
public V getValue();
}
public class MyMapImpl<K, V> implements MyMap<K, V> {
private K key;
private V value;
public MyMapImpl(K key, V value) {
this.key = key;
this.value = value;
}
@Override
public K getKey() { return key; }
@Override
public V getValue() { return value; }
}
public static void myMapExample() {
MyMap<String, Integer> mp1 = new MyMapImpl<String, Integer>("Even", 0);
MyMap<String, String> mp2 = new MyMapImpl<String, String>("Hello", "World");
MyMap<Integer, Integer> mp3 = new MyMapImpl<Integer, Integer>(123, 456);
System.out.println(mp1);
System.out.println(mp2);
System.out.println(mp3);
}
/* Output */
key: Even, value: 0
key: Hello, value: World
key: 123, value: 456
- ? is not a Generic Type Parameter
- ? is not a Generic Type Parameter
- ? is not a Generic Type Parameter
- ? is similar with Number, String, Integer, etc. they are all actual types.
- ? can be regarded as the parent of all types, like Object.
Symbol | Description |
---|---|
? | Uncertain java type |
Symbol | Description |
---|---|
? | Unbounded Wildcard, only the methods in the Object class can be used |
<? extends T> |
Upper Bounded Wildcard |
<? super T> |
Lower Bounded Wildcard |
There is the Fruit class, and its extended class Apple class.
class Fruit {
}
class Apple extends Fruit {
}
Then there is a simple container: Plate class. A generic "thing" can be put on the plate. Then the plate can do two simple actions: "put" and "take", which are set() and get() methods.
class Plate<T> {
private T item;
public Plate(T item) {
this.item = item;
}
public void set(T item) {
this.item = item;
}
public T get() {
return item;
}
}
If we define a "Fruit Plate", logically the apple can be put on fruit plate. Unfortunately, the compiler does not allow this operation.
Plate<Fruit> plate = new Plate<Apple>(new Apple());
// Compilation error: "Plate with apples" cannot be converted to "Plate with fruits".
In fact, the logic of the compiler is:
- Apple IS-A Fruit
- Plate with apples NOT-IS-A Plate with fruits
Even if there is an inheritance relationship between the things (Apple and Fruit) in the container (Plate), there is no inheritance relationship between the containers (Plate with apples and Plate with fruits). That is because Generic is Invariant.
Therefore, we need to use Bounded Wildcards <? extends T> and <? super T> to make the relationship between "Plate with apples" and " and "Plate with fruits".
The following code is "Upper Bounded Wildcard":
Plate<? extends Fruit>
Popular explanation:
- => A plate that can put Fruit and everything that is extended from Fruit.
- => A plate that can put any Fruits.
- The biggest difference between
Plate<? extends Fruit>
andPlate<Apple>
is:Plate<? extends Fruit>
is the super class of Plate and Plate. - This means that we can assign the "Plate with apples" to the "Plate with fruits".
Plate<? extends Fruit> plate = new Plate<Apple>(new Apple());
If we extend Fruit and Apple, Food includes Fruit and Meat, Fruit includes Apple and Banana, Meat includes Pork and Beef, and Apple includes Green Apple and Red Apple.
// Level 1
class Food{}
// Level 2
class Fruit extends Food{}
class Meat extends Food{}
// Level 3
class Apple extends Fruit{}
class Banana extends Fruit{}
class Pork extends Meat{}
class Beef extends Meat{}
// Level 4
class RedApple extends Apple{}
class GreenApple extends Apple{}
In this system, the Upper Bounded Wildcard Plate<? extends Fruit>
covers the blue area in the figure below.
The following code is "Lower Bounded Wildcard":
Plate<? super Fruit>
It expresses the opposite concept: A plate that can put Fruit and everything is the super class of Fruit. Plate<? super Fruit>
is the super class of Plate<Fruit>
, but not the super class of Plate.
In this system, the Lower Bounded Wildcard Plate<? super Fruit>
covers the red area in the figure below.
Bounded Wildcards make it easier to convert between different Java Generics. But don't forget that such conversion also has certain side effects. Some methods of the container may be invalid.
From the examples above, there are two methods in the Plate class, set()
new thing to the Plate, and get()
thing from the Plate.
class Plate<T> {
private T item;
public Plate(T item) {
this.item = item;
}
public void set(T item) {
this.item = item;
}
public T get() {
return item;
}
}
- set() method is invalid.
- get() method works normally.
Plate<? extends Fruit> plate = new Plate<Apple>(new Apple());
// Cannot store elements in
plate.set(new Fruit()); // Error
plate.set(new Apple()); // Error
// Can only take elements out
// The elements that get from the Plate can only be stored in Fruit or its super class
Apple newFruit1 = plate.get(); // Error
Fruit newFruit2 = plate.get();
Object newFruit3 = plate.get();
For the code Plate<? extends Fruit>
, the compiler only knows that the container Plate is Fruit or its extended class, but it does not know what type it is. It could be Fruit, Apple, Banana, RedApple, GreenApple, etc.
For the code new Plate<Apple>(new Apple())
, although the assignment here is Apple, the Plate is not marked with "Apple". Instead, it is marked with a placeholder: CAP#1, to indicate that a Fruit or its extended class is obtained. The specific class is not clear.
Therefore, the compiler doesn't know whether Apple, Banana, RedApple, GreenApple, etc. can match this CAP#1, so they are not allowed to store in.
Lower Bounded Wildcard <? super T> can store elements in and take elements out, but the elements taken out can only be assigned to Object
- set() method works normally.
- get() method is partially invalid.
Plate<? super Fruit> plate = new Plate<Fruit>(new Fruit());
// Can store elements in
p.set(new Fruit());
p.set(new Apple());
// The elements taken out can only be assigned to Object
Apple newFruit1 = plate.get(); // Error
Fruit newFruit2 = plate.get(); // Error
Object newFruit3 = plate.get();
Because the lower bound specifies the lower limit of the minimum granularity of the element, it is actually the container that relaxes its control over the type of element.
Since the element is Fruit or the super class of Fruit, the element can be stored in the container Plate as long as the granularity of the element is smaller than Fruit. But the elements taken out can only be assigned to Object, all the information of the type of the element is lost.
- Take elements in frequently: Upper Bounded Wildcard
<? extends T>
. - Store elements out frequently: Lower Bounded Wildcard
<? super T>
.
Peer Learning
Codecrunch Contributions
Piazza Contributions
Wiki Contributions
Guides
Setting Up Checkstyle
Setting Up Java
Setting Up MacVim
Setting Up Sunfire
Setting Up Unix For Mac
Setting Up Unix For Windows
Setting Up Vim
Setting up SSH Config
CS2030 Contents
Lecture 1 SummaryCompile-run vs Run-time Summary
Quick Guide To Abstraction
Generics and Variance of Types
Comparable vs Comparator
Summary of completable future
CS2030S Notes
ELI5 Optional.map vs Optional.flatMap
PECS Example Code
Java Collection Framework (Iterator)
Generic
Generic Type Parameter and Generic Wildcard
Calculator
Lambda-Expression
Single Abstract Method (SAM)
Method Reference
Functional Interfaces 2
Simple Usage of Sandbox
Associative-but-not-commutative
Higher Order function
Functional Programming
Calculator With Functor
Eager Evaluation VS Lazy Evaluation
Simple Usage of Lazy Evaluation
Lazy Evaluation for LazyList
Lazy Evaluation for BinaryTree
Stream
Parallel Stream
Optional
Simple Usage of Stream
Asynchronous Programming
Notes on CompletableFuture
Notes on CompletableFuture 2
Simple Usage of CompletableFuture
Mind Map
Exception Handling
Links
CS2030 Java Style Guide
CS2030 Javadoc Specification
JDK 11 Download Link
JDK 11 API Docs
Codecrunch
Piazza Forum