欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

JAVA泛型

时间:2023-07-31
文章目录

前言一、为什么使用泛型?二、泛型标记三、泛型使用

3.1泛型方法3.2泛型类3.3泛型接口 四、对泛型上限的限定五、对泛型下限的限定六、类型擦除总结


前言

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


一、为什么使用泛型?

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

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

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


二、泛型标记

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

在使用泛型之前,我们先来了解一下有哪些泛型标记

类型通配符使用?表示所有具体的参数类型,例如List<?>在逻辑上是List、List等所有List<具体类型参数>的父类。

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


三、泛型使用 3.1泛型方法

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

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

3.3泛型接口

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

//定义泛型接口public interface InterfaceGeneral{ public T getId();}//定义泛型接口实现类public class ImplInterfaceGeneral implements InterfaceGeneral{ @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 { public T price; public void show(Demo<? extends Number> demo) { System.out.println(demo.price); } public static void main(String[] args) { Demo extendDemo = new Demo(); extendDemo.show(extendDemo); }}

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

Demo extendDemo = new Demo(); extendDemo.show(extendDemo);


五、对泛型下限的限定

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

public class Demo { public T price; public void show(Demo<? super Integer> demo) { System.out.println(demo.price); } public static void main(String[] args) { Demo superDemo = new Demo(); superDemo.show(superDemo); }}

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

Demo superDemo = new Demo(); superDemo.show(superDemo);


六、类型擦除

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

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

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

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

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

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


泛型类被类型擦除后,相应的类型就被替换成 Object 类型或者上限类型.

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


总结

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


Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。