泛型的介绍与原理
一、什么是泛型
泛型的本质是参数化类型,意思就是所要使用的参数的类型也是个参数,是未确定的,可以是任意的类型(不能是基本数据类型)
二、泛型的意义
- 安全性:在编译期间就能确定类型,不用担心ClassCastException异常
- 复用性:合并了同类型的处理代码的重用率
- 效率提高:不用再因为Object强转的装箱和开箱
- 提升可读性:从编码阶段就显式地知道泛型集合、泛型方法处理的类型是什么
三、使用泛型的三种方式
3.1、泛型类
格式:修饰符 + class + 类名 + <?>()
例如:public class Student ()
参数类型有规范,通常用大写字母表示
- E - Element(在集合中使用)
- T - Type(java类)
- K - Key(键)
- V - Value(值)
- N - Number(数值类型)
- ?- 表示不确定的java类型
package com.cjian.generic;
/**
* @Author: cjian
* @Date: 2023/5/30 10:33
* @Des:
*/
public class GenericClass <T> {
private T type;
public T getType() {
return type;
}
public void setType(T type) {
this.type = type;
}
public GenericClass(T type) {
this.type = type;
}
public static void main(String[] args) {
GenericClass<String> g1 = new GenericClass<>("string");
System.out.println(g1.getType());
GenericClass<Integer> g2 = new GenericClass<>(1);
System.out.println(g2.getType());
}
}
3.2、泛型接口
与泛型类类似
package com.cjian.generic;
/**
* @Author: cjian
* @Date: 2023/5/30 10:39
* @Des:
*/
public interface GenericInterface<T> {
void run(T value);
}
class StringImpl implements GenericInterface<String> {
@Override
public void run(String value) {
System.out.println(value);
}
}
class IntegerImpl implements GenericInterface<Integer> {
@Override
public void run(Integer value) {
System.out.println(value);
}
}
3.3、泛型方法
格式:修饰符 + <?> + 返回值类型 + 方法名()
例子:public void show (T t)
方案一:使用类名后面定义的泛型(所有方法都能用)
方案二:在方法申明上定义自己的泛型(只有本方法能用)
package com.cjian.generic;
import java.util.ArrayList;
/**
* @Author: cjian
* @Date: 2023/5/30 9:51
* @Des: 使用泛型打印不同类型的数组
*/
public class Demo {
public static void main(String[] args) {
String[] strings = {"a", "b", "c"};
Integer[] integers = {1, 2, 3};
Double[] doubles = {1.1, 2.2, 3.3};
printArr(strings);
printArr(integers);
printArr(doubles);
test1();
}
public static <E> void printArr(E[] arr) {
for (E e : arr) {
System.out.printf("%s ", e);
}
System.out.println();
}
public static void test1() {
ArrayList<Integer> arr = new ArrayList<>();
arr.add(1);
int o1 = arr.get(0);
}
}
小细节:E…e代表可变参数,可传任意个数参数
四、泛型通配符
//表示类型参数可以是任何类型
public class GenericClass<?>{}
//表示类型参数必须是A或者是A的子类
public class GenericClass<T extends A>{}
//表示类型参数必须是A或者是A的超类型
public class GenericClass<T supers A>{}
五、类型擦除(重点)
5.1、什么是类型擦除
大家都知道java泛型是伪泛型,这是因为在java编译期间,所有泛型信息都会被擦掉。泛型基本上都是在编译器这个层次上实现的,在生成的字节码文件中是不包含泛型的基本信息的,使用泛型的时候加上类型参数,在编译器编译的时候会去掉,这就是类型擦除
一句话概括:在编译期完成类型检查后,把某个具体的泛型引用擦除成了Object而丢失了它运行时所赋予的类型信息
5.2、为什么要类型擦除
java5才引入了泛型,在此之前都是没有的,引入的目的是为了解决遍历集合的时候总要手动强转类型
List cats = loadCats(); // 5之前没有范型哦
for (Object obj : cats) {
Cat cat = (Cat) obj; // 所以这里总是需要强转
cat.meow();
}
为了让JVM向下兼容,就给出了类型擦除的下下策