π‘
Generics
refers to a mechanism where a specificdata type
ofclasses
,methods
, orinterfaces
is defined at the time ofinstantiation
rather than at the time oftype definition
.
Generics
enables the compile-time
data type check
leading to the lowered risk of runtime error
potentially induced from a type-casting.
Generics
also increases code flexibility
, code reusability
and type safety
, as the data type
is defined at instantiation
.
A generic class
grammatically follows the below format:
class CLASS_NAME<T1, T2, ..., Tn> { /* ... */ }
The above format can be further elaborated as below where the type parameter
, T
, also becomes the type of a instance variable
, and the return type
of the two methods
, a getter()
and a setter()
.
Single Type Parameter
public class Box<T> {
private T t;
public void set(T t) {
this.t = t;
}
public T get() {
return t;
}
}
Results
public class Main{
public static void main(String[] args) {
Box<String> stringBox = new Box<>();
Box<Integer> integerBox = new Box<>();
System.out.println(stringBox.get()); // "";
System.out.println(integerBox.get()); // 0
stringBox.setter("hello");
integerBox.setter(1);
System.out.println(stringBox.get()); // "hello";
System.out.println(integerBox.get()); // 1
stringBox.setter(3240) // compile error
integerBox.setter("hello") // compile error
}
}
The same principles strictly applies to the multiple type parameters
where the below can be a good practical example.
Multiple Type Parameters
public class OrderedPair<K, V> {
private K key;
private V value;
public OrderedPair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
}
Results
OrderedPair<String, Integer> p1 = new OrderedPair<>("Even", 8);
OrderedPair<String, String> p2 = new OrderedPair<>("hello", "world");
Oracle Available at here
π‘
Wildcard
represents an unknown type and can be programmatically represented as the question mark (?).
Wildcard
can be extensively used from a type parameter
to field
, local variable
, or even as a return type
. Wildcard
, if appropriately used, enables developers to write flexible codes and increases code reusability by preventing the creation of duplicate code handling varying type definition
cases.
Essentially, wildcard
can be further specified by implementing its upper or lower boundaries. The upper boundaries define all the subclasses
that either directly or indirectly inherit a given reference type
. On the contrary, the lower boundaries require a provided type parameter
to be a direct or an indirect parent class of a reference type
.
In Java
, programmatically, the upper boundaries can be implemented via the extends
keyword while the lower boundaries can be achieved via the super
keyword.
The below could be a good theoretical example for the upper bounded wildcard
as the List
in the method parameter
only allows the List
execution towards subclasses
of the Hello class
.
Upper Bounded Wildcard
public static void example(List<? extends Hello> list) { /* ... */ }
π‘ The
upper & lower boundaries
can be also applied totype parameters.
Upper Bounded Type Parameter
public class Sample<T super Hello> {
...
}
All the above principles of generic
are well applicable to the interfaces
and methods
.
Generic Interfaces
may be exactly the same to the generic classes
where all the above discussed principles strictly apply to the generic interfaces
.
A good example could be below:
Generic Interface
public interface Pair<K, V> {
public K getKey();
public V getValue();
}
public class OrderedPair<K, V> implements Pair<K, V> {
private K key;
private V value;
public OrderedPair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
}
Oracle Available at here
Generic Methods
are defined as themethods
in which a single or multipletype parameters
within the scope of themethods
are independently specified.
The syntax for the generic methods
are simply the typical method signature followed by the generic diamond
, <>
, where the diamond
is placed after an access modifier
.
The below could be a good example of a generic method
:
Generic Method
public class Sample<T> {
// the below T differs from the above class T
// where the below T has an independent scope within the method
public <T> void example(T value) {
System.out.println("Provided Value is: " + value);
}
}
Given the above generic method
, its invocation can be done either via explicit invocation where the type reference
is explicitly provided or via inference where the Java
infers the right data type
from the provided parameters
.
Results
public class Main {
public static void main(String[] args) {
Main main = new Main();
main.executeExample();
}
public void executeExample() {
Sample<String> sample = new Sample();
sample.<Integer>example(1); // 1
// or by inference
sample.example(1); // 1
}
}
Same applies to a static method
where it can be invoked implicitly and explicitly.
Generic Static Method
public class Sample {
public static <T> void example(T value) {
System.out.println("Provided Value is: " + value);
}
}
βοΈ
Type Erasure
refers to a mechanism in which theJava Compiler
removes alltype parameters
and replaces them withgeneric types
with their bounds orObjects
if unbounded.
Type Erasure
introduces type casts
where necessary to ensure type safety
and generates bridge methods
to maintain polymorphism
in classes
and interfaces
that inherits or implements other classes
and interfaces
with generic types
as well. Type Erasure
ensures that no new classes
are created for parameterised types
; consequently, generics
incur no runtime overhead
.
For instance, the typical Type Erasure
happens to be the following:
Type Erasure (Unbounded Type Parameter)
// source code
public class Node<T> {
private T data;
private Node<T> next;
public Node(T data, Node<T> next) {
this.data = data;
this.next = next;
}
public T getData() { return data; }
// ...
}
// type erased code
public class Node {
private Object data;
private Node next;
public Node(Object data, Node next) {
this.data = data;
this.next = next;
}
public Object getData() { return data; }
// ...
}
Type Erasure (Bounded Type Parameter)
// source code
public class Node<T extends Comparable<T>> {
private T data;
private Node<T> next;
public Node(T data, Node<T> next) {
this.data = data;
this.next = next;
}
public T getData() { return data; }
// ...
}
// type erased code
public class Node {
private Comparable data;
private Node next;
public Node(Comparable data, Node next) {
this.data = data;
this.next = next;
}
public Comparable getData() { return data; }
// ...
}
The above type erasure
execution, however, can be problematic under the below case where a generic class
is extended to another class.
// source code
public class Node<T> {
public T data;
public Node(T data) { this.data = data; }
public void setData(T data) {
System.out.println("Node.setData");
this.data = data;
}
}
public class MyNode extends Node<Integer> {
public MyNode(Integer data) { super(data); }
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
}
// type erased code
public class Node {
public Object data;
public Node(Object data) { this.data = data; }
public void setData(Object data) {
System.out.println("Node.setData");
this.data = data;
}
}
public class MyNode extends Node {
public MyNode(Integer data) { super(data); }
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
}
The type-erased
Node and MyNode class
now have a different method signature
to the setData() method where a method overriding
or a polymorphism
with the generics
fails to comply at the runtime
.
To preserve the polymorphism
, Java Compiler
generates a bridge method
in which it links the two methods where they no longer happen to comply with a polymorphism
.
Below is the generated bridge method
:
Bridge Method
class MyNode extends Node {
// Bridge method generated by the compiler
//
public void setData(Object data) {
setData((Integer) data);
}
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
// ...
}
And given the above generated bridge method
, the below code execution incurs ClassCastException
followed by wrong type-casting
within the bridge method
((Integer) "Hello")
.
MyNode mn = new MyNode(5);
Node n = mn; // A raw type - compiler throws an unchecked warning
// Note: This statement could instead be the following:
// Node n = (Node)mn;
// However, the compiler doesn't generate a cast because
// it isn't required.
n.setData("Hello"); // Causes a ClassCastException to be thrown.
Integer x = (Integer)mn.data;
Oracle All Available at here
μλ°μ μ
Oracle
G Market (1)
G Market (2)
F-Lab (1)
F-Lab (2)