引出泛型
先来看下面一串代码
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本身或者他的父类
328

被折叠的 条评论
为什么被折叠?



