目录
一、概念
泛型是JDK5引入的新特性,旨在解决参数转换问题。泛型能使得程序员在编译期间对非法的数据类型进行检查,提高了程序的安全性。泛型的本质是将数据类型参数化。
二、泛型定义
泛型主要有三种放手:类、接口和方法。
2.1 泛型类
//此处T可以随便写为任意标识,常⻅的如T、E、K、V等形式的参数常⽤于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{
private T key;
public Generic(T key) {
this.key = key;
}
public T getKey(){
return key;
}
}
实例化泛型类:
Generic<Integer> genericInteger = new Generic<Integer>(1);
2.2 泛型接口
public interface Generator<T> {
public T method();
}
实现泛型接口,不指定类型:
class GeneratorImpl<T> implements Generator<T>{
@Override
public T method() {
return null;
}
}
实现泛型接口,指定类型:
class GeneratorImpl<T> implements Generator<String>{
@Override
public String method() {
return "hello";
}
}
2.3 泛型方法
泛型方法,不指定类型:
public static < E > void printArray( E[] inputArray ){
for ( E element : inputArray ){
System.out.printf( "%s ", element );
}
System.out.println();
}
泛型方法,限定类型:
public static < E extends Comparable<E>> void printArray( E[] inputArray ){
for ( E element : inputArray ){
System.out.printf( "%s ", element );
}
System.out.println();
}
调用中,泛型方法可以指定类型,也可以不指定类型:
- 在不指定泛型的情况下,泛型变量的类型为该方法中的几种类型的同一父类的最小级,直到 Object。
- 在指定泛型的情况下,该方法的几种类型必须是该泛型的实例的类型或者其子类。
public class Test {
public static void main(String[] args) {
/**不指定泛型的时候*/
int i = Test.add(1, 2); //这两个参数都是Integer,所以T为Integer类型
Number f = Test.add(1, 1.2); //这两个参数一个是Integer,以风格是Float,所以取同一父类的最小级,为Number
Object o = Test.add(1, "asd"); //这两个参数一个是Integer,以风格是Float,所以取同一父类的最小级,为Object
/**指定泛型的时候*/
int a = Test.<Integer>add(1, 2); //指定了Integer,所以只能为Integer类型或者其子类
int b = Test.<Integer>add(1, 2.2); //编译错误,指定了Integer,不能为Float
Number c = Test.<Number>add(1, 2.2); //指定为Number,所以可以为Integer和Float
}
//这是一个简单的泛型方法
public static <T> T add(T x,T y){
return y;
}
}
三、类型擦除
3.1 概念
类型擦除是指在编译起见擦除泛型的所有信息,并提供相应的原始类型,在字节码中不保留泛型信息,在使用泛型时再加上类型参数。
3.2 需要关注的几个问题
3.2.1 原始类型
类型擦除后,原始类型使用其限定的类型(第一个边界类型变量),若无限定,则使用Object。
3.2.2 先检查再编译
在编译前,会先进行检查,再进行擦除,避免擦除后类型一致,使得其他类型的数据存储进来合法的问题,保证了泛型变量的限定。
其次,类型检查针对的是引用。也就是说,编译前会对引用进行检查。
3.2.3 自动类型转换
由于在字节码中会插入强制类型转换,因此,即使类型擦除后,所有的泛型类型变量都被替换成了原始类型,但是在调用的时候,仍然会自动类型转换。
3.2.4 不能使用基本数据类型
因为在类型擦除后,泛型变量替换成了原始类型,如:Object,而Object无法存储基本数据类型。
3.3.5 instanceof
由于编译前会只对引用进行检查,因此,类型擦除后,泛型信息被原始类型替换,不适合使用instanceof。
3.3.6 泛型静态方法和静态类
泛型类中的静态方法和静态变量不可以使用泛型类所声明的泛型类型参数。
因为静态类中,静态变量和静态方法的调用不需要创建对象,而泛型类中的泛型信息是在实例化对象中定义的。
因此,静态方法要定义时,需要使用自己本身定义的泛型信息。
例子:
public class Test2<T> {
public static T one; //编译错误
public static T show(T one){ //编译错误
return null;
}
}
public class Test2<T> {
public static <T >T show(T one){ //这是正确的
return null;
}
}
四、案例实践
4.1 泛型类实践
奖池类
/**
* project: 数据结构与算法
* target:模拟奖池
* Description: 实现奖品的随机抽奖
* author: Archie
* Date: 2022/6/20 12:14
*/
public class LuckPool {
private ArrayList<Product> productArrayList;
public LuckPool() {
}
public LuckPool(ArrayList<Product> productArrayList) {
this.productArrayList = productArrayList;
}
public ArrayList<Product> getProductArrayList() {
return productArrayList;
}
public void setProductArrayList(ArrayList<Product> productArrayList) {
this.productArrayList = productArrayList;
}
//随机抽奖方法
public Product getRamdonProduct(){
Random r = new Random();
return productArrayList.get(r.nextInt(productArrayList.size()));
}
}
泛型奖品类
public class Product<T> {
private String type;
private T elem;
public Product(String type, T elem) {
this.type = type;
this.elem = elem;
}
public Product() {
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public T getElem() {
return elem;
}
public void setElem(T elem) {
this.elem = elem;
}
}
测试类
public class text01 {
public static void main(String[] args) {
ArrayList<Product> productArrayList = new ArrayList<>();
productArrayList.add(new Product("现金",10000));
productArrayList.add(new Product("现金",20000));
productArrayList.add(new Product("现金",888888));
productArrayList.add(new Product("电子产品","iphone13"));
productArrayList.add(new Product("自习室体验卡","全年免费"));
LuckPool luckPool = new LuckPool(productArrayList);
//抽奖五次
for(int i = 1;i <= 5;i++){
System.out.println("第"+i+"次抽到的奖品类型是:"+luckPool.getRamdonProduct().getType());
System.out.println("具体奖品为:"+luckPool.getRamdonProduct().getElem());
System.out.println("----------------------------");
}
}
}
测试结果
第1次抽到的奖品类型是:现金
具体奖品为:10000
----------------------------
第2次抽到的奖品类型是:现金
具体奖品为:全年免费
----------------------------
第3次抽到的奖品类型是:自习室体验卡
具体奖品为:iphone13
----------------------------
第4次抽到的奖品类型是:自习室体验卡
具体奖品为:10000
----------------------------
第5次抽到的奖品类型是:现金
具体奖品为:iphone13
----------------------------
小结:
在奖品类中使用了泛型类,是因为奖品的类型会有很多种,比如,现金的Integer或者电子产品等的String,所以将奖品类型参数化,因此使用泛型。
参考资料
https://blog.csdn.net/wisgood/article/details/11762427