时间:2021-05-19
引出泛型
我们通过如下的示例,引出为什么泛型的概念。
public class Test { public static void main(String[] args) { List list = new ArrayList(); list.add("abc"); list.add(2); for (int i = 0; i < list.size(); i++) { String name = (String) list.get(i); // error System.out.println("name:" + name); } }}当获取列表中的第二个元素时,会报错,java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String。这是常见的类型转换错误。
当我们将元素放入到列表中,并没有使用指定的类型,在取出元素时使用的是默认的 Object 类型。因此很容易出现类型转换的异常。
我们想要实现的结果是,集合能够记住集合内元素各类型,且能够达到只要编译时不出现问题,运行时就不会出现 java.lang.ClassCastException 异常。泛型刚好能满足我们的需求。
什么是泛型?
泛型,即参数化类型。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
泛型的本质是为了参数化类型,即在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型。在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口和泛型方法。
泛型的特点
Java 语言中引入泛型是一个较大的功能增强。不仅语言、类型系统和编译器有了较大的变化,已支持泛型,而且类库也进行了大翻修,所以许多重要的类,比如集合框架,都已经成为泛型化的了。这带来了很多好处:
命名类型参数
推荐的命名约定是使用大写的单个字母名称作为类型参数。对于常见的泛型模式,推荐的名称是:
如上定义了一个普通的泛型类,成员变量的类型为 T,T的类型由外部指定。泛型方法和泛型构造函数同样如此。
Generic<Integer> genericInteger = new Generic<Integer>(123456); //1Generic<String> genericString = new Generic<String>("key_vlaue"); // 2System.out.println("key is " + genericInteger.getKey());System.out.println("key is " + genericString.getKey());泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。传入的实参类型需与泛型的类型参数类型相同,即为Integer/String。
如上所述,定义的泛型类,就一定要传入泛型类型实参么?
并不是这样,在使用泛型的时候如果传入泛型实参,则会根据传入的泛型实参做相应的限制,此时泛型才会起到本应起到的限制作用。如果不传入泛型类型实参的话,在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型。
如上的代码片段,将会输出如下的结果:
key is 111111
key is 4444
在不传入泛型类型实参的情况下,泛型类中使用的泛型防范或成员变量可以为 Integer 或 String 等等其他任意类型。不过需要注意的是,泛型的类型参数只能是类类型,不能是简单类型。且不能对确切的泛型类型使用 instanceof 操作。对于不同传入的类型实参,生成的相应对象实例的类型是不是一样的呢?具体看如下的示例:
由输出结构可知,在使用泛型类时,虽然传入了不同的泛型实参,但并没有真正意义上生成不同的类型,传入不同泛型实参的泛型类在内存上只有一个,即还是原来的最基本的类型(本例中为 Generic),当然在逻辑上我们可以理解成多个不同的泛型类型。
究其原因,在于 Java 中的泛型这一概念提出的目的,其只是作用于代码编译阶段。在编译过程中,对于正确检验泛型结果后,会将泛型的相关信息擦除。也就是说,成功编译过后的 class 文件中是不包含任何泛型信息的。泛型信息不会进入到运行时阶段。
泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。
通配符
Ingeter 是 Number 的一个子类,同时 Generic<Ingeter> 与 Generic<Number> 实际上是相同的一种基本类型。那么问题来了,在使用 Generic<Number> 作为形参的方法中,能否使用Generic<Ingeter> 的实例传入呢?在逻辑上类似于 Generic<Number> 和 Generic<Ingeter> 是否可以看成具有父子关系的泛型类型呢?下面我们通过定义一个方法来验证。
进行如下的调用
Generic<Integer> genericInteger = new Generic<Integer>(123);show(genericInteger); //error Generic<java.lang.Integer> cannot be applied to Generic<java.lang.Number>通过提示信息我们可以看到 Generic<Integer> 不能被看作为 Generic<Number> 的子类。由此可以看出:同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。
我们不能因此定义一个 show(Generic<Integer> obj)来处理,因此我们需要一个在逻辑上可以表示同时是Generic和Generic父类的引用类型。由此类型通配符应运而生。
T、K、V、E 等泛型字母为有类型,类型参数赋予具体的值。除了有类型,还可以用通配符来表述类型,? 未知类型,类型参数赋予不确定值,任意类型只能用在声明类型、方法参数上,不能用在定义泛型类上。将方法改写成如下:
此处 ? 是类型实参,而不是类型形参。即和 Number、String、Integer 一样都是实际的类型,可以把 ? 看成所有类型的父类,是一种真实的类型。可以解决当具体类型不确定的时候,这个通配符就是 ?;当操作类型时,不需要使用类型的具体功能时,只使用 Object 类中的功能。那么可以用 ? 通配符来表未知类型。
泛型上下边界
在使用泛型的时候,我们还可以为传入的泛型类型实参进行上下边界的限制,如:类型实参只准传入某种类型的父类或某种类型的子类。为泛型添加上边界,即传入的类型实参必须是指定类型的子类型。
我们在泛型方法的入参限定参数类型为 Number 的子类。
Generic<String> genericString = new Generic<String>("11111");Generic<Integer> genericInteger = new Generic<Integer>(2222);showKeyValue1(genericString); // errorshowKeyValue1(genericInteger);当我们的入参为 String 类型时,编译报错,因为 String 类型并不是 Number 类型的子类。
类型通配符上限通过形如 Generic<? extends Number> 形式定义;相对应的,类型通配符下限为Generic<? super Number>形式,其含义与类型通配符上限正好相反,在此不作过多阐述。
泛型数组
在 java 中是不能创建一个确切的泛型类型的数组的,即:
如上会编译报错,而使用通配符创建泛型数组是可以的:
List<?>[] ls = new ArrayList<?>[10]; //List<String>[] ls = new ArrayList[10];JDK1.7 对泛型的简化,所以另一种声明也是可以的。
由于JVM泛型的擦除机制,在运行时 JVM 是不知道泛型信息的。泛型数组实际的运行时对象数组只能是原始类型( T[]为Object[],Pair[]为Pair[] ),而实际的运行时数组对象可能是T类型( 虽然运行时会擦除成原始类型 )。成功创建泛型数组的唯一方式就是创建一个被擦出类型的新数组,然后对其转型。
在运行时,数组对象的出口做转型输出,入口方法在编译期已实现类型安全,所以出口方法可以放心强制类型转换,保证成功。
小结
本文主要讲了 Java 泛型的相关概念和应用。泛型使编译器可以在编译期间对类型进行检查以提高类型安全,减少运行时由于对象类型不匹配引发的异常。由泛型的诞生介绍相关的概念,在保证代码质量的情况下,如何使用泛型去简化开发。
以上所述是小编给大家介绍的Java泛型及其应用详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
在Java中,泛型的引入是为了在编译时提供强类型检查和支持泛型编程。为了实现泛型,Java编译器应用类型擦除实现:1、用类型参数(typeparameters)
java泛型的详解及实例Java在1.5版本中增加了泛型,在没有泛型之前,从集合中读取每一个对象都需要进行强转,如果一不小心插入了类型错误的对象,在运行时就会报
泛型是Java开发中常用的技术,了解泛型的几种形式和实现泛型的基本原理,有助于写出更优质的代码。本文总结了Java泛型的三种形式以及泛型实现原理。泛型泛型的本质
本文实例讲述了Java泛型类与泛型方法的定义。分享给大家供大家参考,具体如下:Java泛型类的定义一点睛泛型类定义的语法如下:[访问修饰符]class类名称泛型
java泛型方法:泛型是什么意思在这就不多说了,而Java中泛型类的定义也比较简单,例如:publicclassTest{}。这样就定义了一个泛型类Test,在