参考视频(B 站 UP 主 AlbertShen
):https://www.bilibili.com/video/BV1H94y1a7bJ/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=d55a4a2d31b98315ece09779a92b618b
需求:创建一个类,用来打印 Integer 类型的变量
public class IntegerPrinter {
Integer content;
IntegerPrinter(Integer content) {
this.content = content;
}
public void print() {
System.out.println(content);
}
}
接下来在 main
方法中进行调用:
public class Main {
public static void main(String[] args) {
IntegerPrinter printer = new IntegerPrinter(123);
printer.print();
}
}
这个时候,如果我们想换个类型打印,比如打印 String
类型,此时就不能再用这个类了,必须要新建一个字符串的打印类:
public class StringPrinter {
String content;
StringPrinter(String content) {
this.content = content;
}
public void print() {
System.out.println(content);
}
}
这里我们会发现一个问题,如果我们想新打印一个类型的话,就需要新建一个打印类,会给代码带来很多的重复性,是需要避免的。
正因如此,Java 中引入了 Generics 泛型的概念,以至于只需要创建一个类,就可以处理所有的类型,在类名后面通过 <>
来声明泛型 T
:
public class Printer<T> {
T content;
Printer(T content) {
this.content = content;
}
public void print() {
System.out.println(content);
}
}
在调用时,需要在声明时使用 <>
来定义需要打印的类型,比如我们要答应 Integer
类型:
public class Main {
public static void main(String[] args) {
Printer<Integer> printer = new Printer(123);
printer.print();
}
}
如果此时我们想打印 String
类型的变量,不需要再去单独写一个 String
类型的打印类,只需要在声明时将泛型定义为 String
类型即可:
public class Main {
public static void main(String[] args) {
Printer<String> printer = new Printer("hello world");
printer.print();
}
}
❗注意:
<>
中的类型不能为 Java 中的基本数据类型,必须是经过包装后的类型。
在声明泛型时也可以定义多个参数的类型,比如:
public class Printer<T, K> {
T content;
K content2;
Printer(T content, K content2) {
this.content = content;
this.content2 = content2;
}
public void print() {
System.out.println(content);
System.out.println(content2);
}
}
在使用时,就需要传入两个类型:
public class Main {
public static void main(String[] args) {
Printer<String, Integer> printer = new Printer("hello world", 123);
printer.print();
}
}
在定义泛型时,可以对泛型进行约束,比如约束传入的类型必须是某个类型的子类:
public class Printer<T extends Vehicle> {
T content;
Printer(T content) {
this.content = content;
}
public void print() {
System.out.println(content);
}
}
此时,在声明该类时,必须传入 Vehicle
的子类才行:
public class Main {
public static void main(String[] args) {
Printer<Car> printer = new Printer(new Car());
printer.print();
}
}
public class Main {
public static void main(String[] args) {
Printer<Bus> printer = new Printer(new Bus());
printer.print();
}
}
<T extends Vehicle>
这个在 Java 中称为 Bounding Generics,即有界限的泛型。
当然,我们也可以用接口来进行约束,假如现在有一个接口 Thing
,可以在声明泛型时约束传入的类必须是 Thing
的子类型,Thing
虽然是接口,但声明接口的子类型还是用 extends
关键字:
public class Printer<T extends Thing> {
T content;
Printer(T content) {
this.content = content;
}
public void print() {
System.out.println(content);
}
}
也可以用类和接口来共同约束,类和接口中间用 &
符号连接,注意类必须放在接口前面:
public class Printer<T extends Vehicle & Thing> {
T content;
Printer(T content) {
this.content = content;
}
public void print() {
System.out.println(content);
}
}
此时传入的类型 T
必须同时满足是 Vehicle
的子类,同时实现了 Thing
接口。因为显式声明了 T
必须是 Vehicle
的子类,那么 T
类型的 content
变量就可以拿到 Vehicle
里定义的方法和变量。
public class Vehicle {
String brand;
String color;
public String getBrand() {
return brand;
}
public String getColor() {
return color;
}
}
public class Printer<T extends Vehicle & Thing> {
T content;
Printer(T content) {
this.content = content;
}
public void print() {
System.out.println(content.getColor());
}
}
除了给类型添加泛型外,还可以给方法添加泛型,方法是在返回值类型前面通过 <>
来声明泛型:
public static <T> void pring(T content) {
System.out.pprintln(content);
}
此时该 print
方法就可以打印所有类型的变量了。
如果要对方法的泛型进行约束,方式和对类的泛型进行约束的方式一致:
public static <T extends Vehicle> void pring(T content) {
System.out.pprintln(content);
}
public static <T extends Thing> void pring(T content) {
System.out.pprintln(content);
}
public static <T extends Vehicle & Thing> void pring(T content) {
System.out.pprintln(content);
}
和类泛型一样,方法泛型也可以传入多个参数,声明方式相同:
public static <T, K> void pring(T content, K content2) {
System.out.pprintln(content);
System.out.pprintln(content2);
}
此时,如果我们想写一个方法可以打印存放不同类型元素的 List
,可以采用 ?
通配符的方式来声明泛型:
public static void pring(List<?> content) {
System.out.pprintln(content);
}
也可以进行一些类型上的约束:
public static void pring(List<? extends Vehicle> content) {
System.out.pprintln(content);
}
通配符中还有一种,叫下界通配符(Lower Bounded Wildcard),使用 super
关键字来表示:
public static void pring(List<? super Vehicle> content) {
System.out.pprintln(content);
}
此时传入的类型必须是 Vehicle
本身或者它的父类。