Java泛型总结

一、概念

泛型是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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值