Generics
# The need for Generics
public class List {
private int[] items = new int[10];
private int count;
public void add(int item) {
items[count++] = item;
}
public int get(int index) {
return items[index];
}
}
public class Main {
public static void main(String[] args) {
var list = new List();
list.add(1);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- The problem: We can only use the list to store integers
- To store other types, we need to create XXXList classes, which is not scalable
# A Poor Solution
public class List {
private Object[] items = new Object[10];
private int count;
public void add(Object item) {
items[count++] = item;
}
public Object get(int index) {
return items[index];
}
}
public class Main {
public static void main(String[] args) {
var list = new List();
list.add(1); // == list.ad(Integer.valueOf(1)) transfer a primtive type to a // new instace of the object class
list.add("1");
list.add(new User());
// however
int number = (int) list.get(0);
int text = (int) list.get(1);
// 1. noisy and verbose type translation
// 2. possible invalid cast exception
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# Genric Classes
public class Main {
public static void main(String[] args) {
// we specify the type of parameter when declaring the list object
var list = new GenericList<Integer>()
// this ensures that every elements in that list is of the same type
list.add(1);
int number = list.get(0); // don't need explicit cast
// we get compile-time safety checks
}
}
// T stands for type parameter, it is a class parameter
// it represents the type of parameter we want to store
public class GenericList<T> {
// Java doesn't know what T stands for right now, so use object to replace it
private T[] items = (T[]) new Object[10];
private int count;
public void add(T item) {
items[count++] = item;
}
public T get(int index) {
return items[index];
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# Generics and Primitive Types
- When creating an instance of a generic type, we can only use a reference type as a generic type argument
public class Main {
public static void main(String[] args) {
// int -> Integer
// float -> Float
// boolean -> Booelan
GenericList<Integer> numbers = new GenericList<>();
numbers.add(1); // boxing: put this primitive valie inside a box
int number = number.get(0) // unboxing: extract the int value
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
- The type of elements stored in that list can be User, Object or String, because they are reference types in Java
- However, we cannot pass the primitive types
- We need to use the wrapper class of those primitive types
# Constraints
- Add a constraint or a restriction on a type parameter
- This constraint can also be an interface like Comparable
// now T can only be the Number class or any of its children classes
// e.g. Integer, Double
public class GenericList<T extends Number & Comparable> {
}
1
2
3
4
2
3
4
# Type Erasure
To show bit code
- Build
- Build project
- Select the class
- From the
Viewmenu- Select
Show Bytecode
- Select
When building projects, Java will erase
If there is no constraints, in bit code, all GenericList are still of object type, which is the same as the previous List class
Java will automatically replace
with Object- Because
Objectis the default class
- Because
The difference is that Java will check data time at compile-time, which is more safe and reliable
After applying Constraints
- Java will replace
with constraints
If there are multiple constrainst
- Java will replace
with the left most constraint
# Comparable Interface
- We use this interface to compare two objects
- With this comparison, we can determine what objects should come first
- Comparable is a generic interface
- Interface Comparable
- Interface Comparable
- This interface has a single method called
compareTowhich takes an object
compareTo(T o)
// compares this object with the specified object fro order
1
2
2
// if not write <User>, Intellij will generate compareTo(Object o)
public class User implements Comparable<User> {
private int points;
public User(int points) {
this.points = points;
}
// the very important compareTo method
@Override
public int compareTo(User other) {
// this < other -> -1
// this == other -> 0
// this > other -> 1
return points - other.points;
}
@Override
public String toString() {
return "Points=" + points;
}
}
public class Main {
public static void main(String[] args) {
var user1 = new User(10);
var user2 = new User(20);
if (user1.compareTo(user2) < 0)
System.out.println("User 1 < User 2");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# Generic Methods
- We can declare a generic method inside a non-generic class
public class Utils {
public static int max(int first, int second) {
return (first > second) ? first : second;
}
// becomes
public static <T extends Comparable<T>> T max(T first, T second) {
return (first.compareTo(second) < 0) ? second : first;
}
}
public class Main {
public static void main(String[] args) {
var max = Utils.max(1, 3);
var maxClient = Utils.max(new User(10), new User(20));
System.out.println(maxClient); // in order to print meaningful info, override toString method
}
}
public class User implements Comparable<User> {
@Override
public String toString() {
return "Points=" + points;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Multiple Type Parameters
public void print(int key, int value) {
System.out.println(key + "=" + value);
}
// becomes
public static <K, V> void print(K key, V value) {
System.out.println(key + "=" + value);
}
public class Main {
public static void main(String[] args) {
Utils.print(1, 10);
}
}
// declare multiple-type parameters
public class KeyValuePair<K, V> {
private K key;
private V value;
public KeyValuePair(K key, V value) {
this.key = key;
this.value = value;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# Generic Classes and Inheritance
// super class
public class User implements Comparable<User> {
private int points;
public User(int points) {
this.points = points;
}
}
// sub class
public class Instructor extends User {
public Instructor(int points) {
super(points);
}
}
public class Main {
public static void main(String[] args) {
var users = new GenericList<Instructor>();
Utils.printUsers(users); // this wrong, remember the bytecode representation of generic class?
// this is tedius
var instructors = new GenericList<Instructor>();
var users = new GenericList<User>();
for (Instructor instructor : instructors)
users.add(instructor)
Utils.printUsers(users);
}
}
public class Utils {
public static void printUser(User user) {
System.out.println(user);
}
// generic class of instructor is not a subtype of user, so need to specify
public static void printUsers(GenericList<User> users) {
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# Wildecards
?Is the wildcard character- It represents an unknown type
public class Utils {
public static void printUser(User user) {
System.out.println(user);
}
// `extends` restrict the type of inputs
// if want to read from the list, use the `extends` keyword
public static void printUsers(GenericList<? extends User> users) {
User x = users.get(0);
}
// if you want to add to the list, use the `super` keyword
public static void printUses(GenericList<? super User> users) {
GenericList<Object> temp = new GenericList<>();
Object x = users.get(0);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
上次更新: 2022/12/04, 16:55:22