Java——泛型
引出泛型
先来看下面一串代码
class MyArrayList {public int usedSize;public int[] arr;public MyArrayList() {this.arr = new int[10];}public void add(int val) {arr[usedSize] = val;usedSize++;}
}public class Test {public static void main(String[] args) {MyArrayList myArrayList = new MyArrayList();myArrayList.add(1);myArrayList.add(2);}
}
可以看到顺序表可以添加整形元素,那么如果要添加String类型的呢
可以看到会报错,那如果想要放字符串类型的元素是不是要再重新写一个MyArrayList类,那是不是也太麻烦了
这里就要引出java中提供的泛型语法
什么是泛型
一般的类和方法,只能使用具体的类型: 要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的代码,这种刻板的限制对代码的束缚就会很大。简单来说就是,泛型:就是适用于许多许多类型。从代码上讲,就是对类型实现了参数化。
语法:
class 泛型类名称<类型形参列表>
泛型类<类型实参> 变量名; // 定义一个泛型类引用
new 泛型类<类型实参>(构造方法实参); // 实例化一个泛型类对象
所以上述代码可以改为
class MyArrayList<T> {public int usedSize;public T[] arr;public MyArrayList() {this.arr = (T[])new Object[10];}public void add(T val) {arr[usedSize] = val;usedSize++;}
}public class Test {public static void main(String[] args) {//存放String类型MyArrayList<String> myArrayList1 = new MyArrayList<>();myArrayList1.add("abc");myArrayList1.add("xyz");//存放int类型MyArrayList<Integer> myArrayList2 = new MyArrayList<>();myArrayList2.add(1);myArrayList2.add(2);}
}
这样就实现了通用
这里还有一个疑问?
这个数组为什么实例化的时候要new 一个object类,还要强制类型转换呢?
这里就要说到泛型的擦除机制了
什么时擦除机制
泛型逻辑语法比较难,具体的泛型擦除机制可以看这篇文章:https://zhuanlan.zhihu.com/p/51452375
在编译的过程当中,将所有的T替换为Object这种机制,我们称为:擦除机制。
所以在编译期间T就会被擦除为object,明明时new的object[] ,T也会被擦除为object,那为啥还要强制类型转换?
编译器的视角:
T
是一个“未知但具体的类型”(比如String
、Integer
等)。
new Object[10]
创建的是Object[]
,而T[]
理论上应该是String[]
、Integer[]
等。直接赋值
T[] arr = new Object[10]
会导致类型不匹配(因为Object[]
不是T[]
的子类型),所以需要强制转换。运行时的实际行为:
由于类型擦除,
T[]
在运行时就是Object[]
,所以强制转换(T[])
实际上什么都不做。但编译器会插入 隐式的类型检查,确保后续对
arr
的操作符合T
的约束(比如不能存入非T
类型的值)。
为什么不能直接new T[] ?
数组在运行时需要知道具体的类型(比如
String[]
和Integer[]
是不同的)。但泛型
T
在运行时被擦除为Object
,无法确定应该创建String[]
还是Integer[]
。如果允许
new T[]
,可能会导致错误的数组存储(比如把Integer
存入String[]
)。所以 Java 强制要求开发者使用
Object[]
+ 强制转换的方式,并自行保证类型安全。
上述泛型的中被擦除后一定时object吗?
不一定的,擦除后变成的类型时T的上界,那什么是泛型的上界?
语法定义:
class 泛型类名称<类型形参 extends 类型上边界>
代码示例:
实例化的时候只能传入类型上边界或者其子类
class Person {}
class Student extends Person {}
class AAA {}
class MyArrayList<T extends Person> {}public class Test {public static void main(String[] args) {MyArrayList<Student> myArrayList1 = new MyArrayList<>();MyArrayList<Person> myArrayList2 = new MyArrayList<>();}
}
如果传入AAA呢,AAA没由继承Person,可见会报错
接下来我们讲通配符
class Message<T> {private T message ;public T getMessage() {return message;}public void setMessage(T message) {this.message = message;}}
public class TestDemo {public static void fun(/*这里的类型接收*/Message<String> temp) {System.out.println(temp.getMessage());}public static void main(String[] args) {Message<String> message = new Message<>();message.setMessage("i love china");fun(message);}
}
看到类型接收的地方,如果换成传入的int类型,这里就会报错
此时也要把 接受类型改为Integer
同样也不具有一般性,这里就要用到通配符了,通配符就是把类型换成 ?(问号)
通配符更改后
public class TestDemo {public static void fun(/*这里的类型接收*/Message<?> temp) {System.out.println(temp.getMessage());}public static void main(String[] args) {Message<String> message1 = new Message<>();Message<Integer> message2 = new Message<>();message1.setMessage("i love china");message2.setMessage(99);fun(message1);fun(message2);}
}
这样就具有通用性了
通配符不仅由上界,同时还具有下界
? extends 类:设置通配符上限
? super 类:设置通配符下限
class Food {}
class Fruit extends Food {}
class Apple extends Fruit {}
class Banana extends Fruit {}
class Message2<T> { // 设置泛型private T message ;public T getMessage() {return message;}public void setMessage(T message) {this.message = message;}}public class TestDemo2 {// 此时使用通配符"?"描述的是它可以接收任意类型,但是由于不确定类型,所以无法修改public static void fun(/*传入类型*/Message2<? extends Fruit> temp){//temp.setMessage(new Banana()); //仍然无法修改!//temp.setMessage(new Apple()); //仍然无法修改!System.out.println(temp.getMessage());}public static void main(String[] args) {Message2<Apple> message = new Message2<>() ;message.setMessage(new Apple());fun(message);Message2<Banana> message2 = new Message2<>() ;message2.setMessage(new Banana());fun(message2);}}
此时那个传入类型必须是fruit或者其子类,同时接受的值也不能修改
通配符的下界
class Food {}
class Fruit extends Food {}
class Apple extends Fruit {}
class Plate<T> {private T plate ;public T getPlate() {return plate;}public void setPlate(T plate) {this.plate = plate;}
}
public class TestDemo3 {public static void fun(Plate<? super Fruit> temp){// 此时可以修改!!添加的是Fruit 或者Fruit的子类temp.setPlate(new Apple());//这个是Fruit的子类temp.setPlate(new Fruit());//这个是Fruit的本身//Fruit fruit = temp.getPlate(); 不能接收,这里无法确定是哪个父类System.out.println(temp.getPlate());//只能直接输出}public static void main(String[] args) {Plate<Fruit> plate1 = new Plate<>();plate1.setPlate(new Fruit());//通配符的下界,不能进行读取数据,只能写入数据。fun(plate1);Plate<Food> plate2 = new Plate<>();plate2.setPlate(new Food());fun(plate2);}}
使用 <? super T>
表示,它表示 “某个未知类型,但必须是 T
或其父类”,上述函数的接受值必须是fruit本身或者他的父类