Java generics

Java泛型是J2 SE1.5中引入的一个新特性,其本质是参数化类型,也就是说所操作的数据类型被指定为一个参数(type parameter)这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。

在Class中应用

传统实现方法

1
2
3
4
5
6
7
8
9
10
11
public class StringPrinter {  
String content;

public StringPrinter(String content) {
this.content = content;
}

public void print() {
System.out.println(content);
}
}
1
2
3
4
5
6
7
8
9
10
11
public class IntegerPrinter {  
Integer content;

IntegerPrinter(Integer content) {
this.content = content;
}

public void print() {
System.out.println(content);
}
}

我们测试下这两个类

1
2
3
4
5
6
7
8
9
10
11
12
@Test  
public void testStringPrinter(){
StringPrinter printer = new StringPrinter("Hello World");
printer.print();
}

@Test
public void testIntegerPrinter(){
IntegerPrinter printer = new IntegerPrinter(123);
printer.print();
}
// 分别在控制台打印 “Hello World” 和 123

上面的例子是我们传统思维的解决方法,下面使用泛型来重构代码

1
2
3
4
5
6
7
8
9
10
11
public class Printer<T> {  
T content;

Printer(T content) {
this.content = content;
}

public void print() {
System.out.println(content);
}
}
1
2
3
4
5
6
7
8
9
10
11
@Test  
public void testStringGenericsPrinter(){
Printer<String> printer = new Printer<>("Hello World");
printer.print();
}
@Test
public void testIntegerGenericsPrinter(){
Printer<Integer> printer = new Printer<>(123);
printer.print();
}
//使用测试用例,同样会在控制台打印"Hello World" 和 123

T占位符号可以是任何字符,也可以传入多个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Printer<T, K> {  
T content;
K content2;

public Printer(T content, K content2) {
this.content = content;
this.content2 = content2;
}

public void print() {
System.out.println(content);
System.out.println(content2);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
@Test  
public void testStringGenericsPrinter() {
Printer<String, Integer> printer = new Printer<>("Hello World", 123);
printer.print();
}
// 控制台输出 => "Hello World" 123
@Test
public void testIntegerGenericsPrinter() {
Printer<Integer, Integer> printer = new Printer<>(123, 456);
printer.print();
}
// 控制台输出 => 123 456

我们也可以对泛型进行一些约束(bounded generics),比如规定传入的泛型类型必须是某一个类型的子类型

1
2
3
4
5
6
7
8
9
10
11
public class Printer<T extends Vehicle> {  
T content;

public Printer(T content) {
this.content = content;
}

public void print() {
System.out.println(content);
}
}
1
2
3
4
5
6
7
8
9
10
11
@Test  
public void testCarGenericsPrinter() {
Printer<Car> printer = new Printer<>(new Car(290000, "Model X"));
printer.print();
}

@Test
public void testBusGenericsPrinter() {
Printer<Bus> printer = new Printer<>(new Bus(35));
printer.print();
}

同时我们可以在约束继承类的同时,可以同样加入到接口实现的约束,但是要注意先后顺序,继承类在前,接口在后

1
2
3
4
5
6
7
8
9
10
11
12
public class Printer<T extends Vehicle & Thing> {  
T content;

public Printer(T content) {
this.content = content;
}

public void print() {
System.out.println(content);
}
}
// 如果传入的类未实现该接口,在会直接报错(e.g. Bus不在类型变量T的范围内)

Method 中的应用

1
2
3
4
5
6
7
8
9
10
public static <T> void print(T content) {  
System.out.println(content);
}

public static void main(String[] args) {
print("Hello World");
print(1234567);
print(12L);
print(new Car(150000,"X5"));
}
1
2
3
4
5
6
7
8
9
10
// 在method中我们同样可以使用bounded generics
public static <T extends Vehicle & Thing> void print(T content ) {
System.out.println(content);
}

// 传入多个泛型参数
public static <T,K> void print(T content, K content2) {
System.out.println(content);
System.out.println(content2);
}

在List中我们可以使用通配符(wildcard)来处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void print(List<?> list) {  
System.out.println(list);
}

public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(123);
list.add(456);
print(list);
ArrayList<String> list2 = new ArrayList<>();
list2.add("Hello");
list2.add("World");
print(list2);
}

通配符的使用场景下我们同样可以引入约束

  • upper bounded wildcard
1
2
3
4
5
6
7
8
9
10
11
public static void print(List<? extends Vehicle> list) {  
System.out.println(list);
}

public static void main(String[] args) {
ArrayList<Car> list = new ArrayList<>();
list.add(new Car(300000,"320"));
list.add(new Car(700000,"750"));
print(list);

}
  • lower bounded wildcard
1
2
3
4
5

public static void print(List<? super Vehicle> list) {
System.out.println(list);
}
// 传入的类型必须是Car本身或者Car的父类,如果传入Bus就会直接报错
1
2
3
4
5
6
备注:博文涉及到的英文单次小结:
Generics
Wildcard
{} Curly Braces
<> Angle Brackets
? Question Mark

Java generics
https://skynetboys.github.io/2023/09/26/Java-generics/
Author
Edison
Posted on
September 26, 2023
Licensed under