理解JAVA泛型,看这一篇


前言

在JAVASE的集合章节已经简单介绍了泛型的概念以及基本使用,本章将继续讲解有关泛型的内容,包括泛型上下限的限定以及类型擦除等内容。


一、为什么使用泛型?

一般的类和方法,只能使用具体的类型,要么是基本类型,要么是自定义的类。如果要编写可以应用多种类型的代码,这种刻板的限制对代码得束缚会就会很大。 -----------《Thinking in Java》

在不使用泛型的情况下我们可以通过引用Object类型来实现参数的任意化,因为Object是所有类的父类。

但是我们仔细想想这样做的话有什么后果?
具体使用时需要进行强制类型转换(父类转成子类),要求开发者明确知道实际参数的引用类型,不然可能引起类型转换错误,而编译期无法识别这种错误,只能在运行期检测(程序运行出错时),显然这不是我们想看到的。

这时候使用泛型的好处就来了:泛型在编译期就能够检查类型是否安全,同时所有强制类型转换都是自动和隐式进行的,提高了代码的安全性和重用性


二、泛型标记

泛型的本质是参数化类型(编译器可以自动定制作用于特定类型的类),提供编译时类型的安全检测机制,该机制允许程序在编译时检测非法的类型。

在使用泛型之前,我们先来了解一下有哪些泛型标记
在这里插入图片描述
类型通配符使用?表示所有具体的参数类型,例如List<?>在逻辑上是List、List等所有List<具体类型参数>的父类。

如果希望将类的继承关系加入到泛型应用中,就需要对泛型做限定,具体的限定有泛型上限和泛型下限的限定。


三、泛型使用

泛型的设计主要参照了C++的模板,旨在能让人写出更加通用化,更加灵活的代码。模板/泛型代码,就好像做雕塑时的模板,有了模板,需要生产的时候就只管向里面注入具体的材料就行,不同的材料可以产生不同的效果,这便是泛型最初的设计宗旨。

3.1泛型方法

将方法的参数类型定义为泛型,在方法内部根据不同类型参数执行不同的处理方法。比如要实现一个能够对字符串(String),整型(Integer),浮点型(Float)比较大小的方法,就可以使用JAVA泛型。

public class Compare {
    public static <T> int compare(T t1, T t2){
        if(t1 instanceof String){
            return ((String) t1).compareTo((String) t2);
        }else if(t1 instanceof Integer){
            return ((Integer) t1).compareTo((Integer) t2);
        }else if(t1 instanceof Float){
            return ((Float) t1).compareTo((Float) t2);
        }
        else{
            return -10;
        }
    }

    public static void main(String[] args) {
        System.out.println(Compare.compare("1","2"));
        System.out.println(Compare.compare(1,2));
        System.out.println(Compare.compare(1.0f,2.0f));
    }
}

在这里插入图片描述

3.2泛型类

泛型类在定义类时在类上定义了泛型,类在使用时可以根据传入的不同参数类型实例化不同的对象。

public class GeneralClass <T>{
    private T t;
    public void add(T t){
        this.t=t;
    }
    public T get(){
        return t;
    }
    public static void main(String[] args) {
        //根据需求初始化不同的需求
        GeneralClass<Integer>genInt=new GeneralClass<>();
        genInt.add(1);
        GeneralClass<String>genString=new GeneralClass<>();
        genString.add("1");
    }
}

3.3泛型接口

和泛型类声明相似,在接口名后面定义了泛型,具体类型一般在实现类中进行声明。

//定义泛型接口
public interface InterfaceGeneral<T>{
    public T getId();
}

//定义泛型接口实现类
public class ImplInterfaceGeneral implements InterfaceGeneral<Integer>{
    @Override
    public Integer getId() {
        Random random=new Random();
        return random.nextInt();
    }
    public static void main(String[] args) {
        ImplInterfaceGeneral implInterfaceGeneral=new ImplInterfaceGeneral();
        System.out.println(implInterfaceGeneral.getId());
    }
}

在这里插入图片描述


四、对泛型上限的限定

在JAVA中使用"?"和"extends"关键字指定泛型的上限,具体用法为<? extends T>,表示该通配符所代表的类型是T类的子类或T接口的子接口

package com.ffyc.forward.generic;

public class Demo<T> {
    public T price;
    /*意思是泛型的类型只能是Number或Number的子类,定义了上限*/
    public void show(Demo<? extends Number> demo) {
        System.out.println(demo.price);
    }  
    public static void main(String[] args) {
        Demo<Integer> extendDemo = new Demo();
        extendDemo.show(extendDemo);
    }
}

如果将类型改为Object,则在编译时就会报错,因为Object不是Number的子类(这就是安全检测机制)

    Demo<Object> extendDemo = new Demo();
    extendDemo.show(extendDemo);

在这里插入图片描述


五、对泛型下限的限定

在JAVA中使用"?"和"super"关键字指定泛型的上限,具体用法为<? superT>,表示该通配符所代表的类型是T类的父类或T接口的父接口

public class Demo<T> {
    public T price;
    
    /*意思是泛型的类型只能是Integer或Integer的父类,定义了下限*/
    public void show(Demo<? super Integer> demo) {
        System.out.println(demo.price);
    }

    public static void main(String[] args) {
        Demo<Number> superDemo = new Demo();
        superDemo.show(superDemo);
    }
}

如果将类型改为String,则在编译时也会报错,因为String不是Integer的父类

        Demo<Number> superDemo = new Demo();
        superDemo.show(superDemo);

在这里插入图片描述


六、类型擦除

泛型是Java 1.5版本才引进的概念,在这之前是没有泛型的。

但是,泛型代码能够很好地和之前版本的代码兼容,那是因为泛型信息只存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除掉,我们称之为一类型擦除。

注意:因此泛型主要用于编译阶段,在编译后生成的JAVA字节码文件中没有包含泛型的类型信息(因此JAVA泛型也称为伪泛型)。

例如:List<Integer>List<String>在经过编译后统一为List
JVM读取的只是List,泛型附加的类型信息对JVM不可见。

例:Demo是一个泛型类,我们查看它在运行时的状态信息可以通过反射

public class Demo<T> {
    public T price;
    
    public static void main(String[] args) throws NoSuchFieldException {
        Demo<Integer> demo = new Demo();
        System.out.println(superDemo.getClass().getField("price").getType());
    }
}

在这里插入图片描述
泛型类被类型擦除后,相应的类型就被替换成 Object 类型或者上限类型.

类型擦除过程
1.查找用来替换类型参数的具体类(一般是Object),如果指定了类型参数上界,则以该上界作为替换的具体类。
2.然后把代码中的类型参数都替换为具体的类。


总结

使用泛型的好处是泛型提供了编译时类型的安全检测机制,可以减少我们程序出错的机会。在编译期就提醒程序员可能出现的错误,同时所有的类型转换都是自动和隐式进行的,开发人员只需学习如何使用即可。泛型的概念有时很抽象,建议在敲写代码中理解泛型给我们带来的便利。关于泛型为什么要类型擦除的问题读者可以自行阅读相关资料深入学习了解。


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JinziH Never Give Up

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值