首页 > 应用开发 > Java > 关于Java泛型,你了解多少?
泛型就是
参数化类型,也就是把所操作的类型看做为一个参数。
为什么需要泛型
在有泛型之前,我们可以创建一个存储Object类型的集合,该集合可以存储任何数据类型对象(8种基本类型除外),在没有泛型的支持下,我们需要明确集合中存储的每一个元素的数据类型,否则,在程序中很容易出现ClassCastException类转换异常。
有了泛型之后,我们在创建一个集合的时候可以指定其存储的数据类型。如当我们创建一个只能存储String类型的ArrayList集合时,当我们往集合中添加Integer类型的数据时,就会编译不通过,保证了集合类型的安全,同时在取出集合数据时消除了类型的强制转换。
本质上这些都是表示通配符,没什么区别,只不过是编码时的一种约定俗成的东西。比如上述代码中的 T ,我们可以换成 A-Z 之间的任何一个 字母都可以,并不会影响程序的正常运行,但是如果换成其他的字母代替 T ,在可读性上可能会弱一些。 通常情况下,T,E,K,V,?是这样约定的:
类是面向对象中最基础的元素,当我们在设计一个类时,不仅需要考虑类的属性和方法,还需要考虑类的
可扩展性,泛型就给我们提高类的可扩展提供了实现。通过泛型我们可以完成对一组类的操作对外开放相同的接口,通过传入不同的数据类型,可以对相应类型的数据进行操作。
泛型类的定义
[访问修饰符] class 类名<泛型标识> { }
泛型类派生子类
如果继承于父类(泛型类)的 子类是泛型类,那么 子类和父类的泛型类型需一致。
class children<T> extends GenericParent<T> { }
如果继承于父类(泛型类)的 子类为非泛型类,那么在子类在声明继承父类时 必须明确指定父类的泛型类型。
class children extends GenenricParent<String> { }
泛型类是我们在实例化类时指明泛型的具体类型,而泛型方法就是我们在调用方法时指明泛型的具体类型, 泛型方法能使方法独立于类而产生变化。
泛型方法的定义
[访问修饰符] <T,E...> 返回值类型 方法名称 (形参列表) { }
注意
public T getName() { return name; }并不是泛型方法。泛型方法例子:
泛型接口的定义
对应的实现
泛型接口的
实现类的定义:
class 实现类名称 [<泛型标识>] implements 接口名称 <泛型标识> { }
注意
如果实现泛型接口的 实现类不是泛型类,则在声明实现泛型接口时 必须明确指定泛型接口的泛型类型。
class interfaceImpl implements GenericInterface<String> { }
class interfaceImpl<T,其他泛型标识...> implements GenericInterface<T> { }
泛型接口例子:
泛型通配符一般使用
“ ?”替代具体的
类型实参。
问题引入
我们都知道Interger是Number的子类,那么在
Generic<Number>作为形参的方法中,能否使用
Generic<Ingeter>的实例传入呢? 我们测试一下:
Generic<Number>的方法,并测试当调用该方法时传入
Generic <Intege> 类型实参的情况
很显然,当我们在形参声明为
Generic<Number>类型的方法中,在调用该方法时传入一个
Generic<Integer>类型的实参时,会编译不通过,即使Integer是Number的子类。
“ ?”的用武之地就来了,使用
Generic<?>来替换掉方法定义时形参中的
Generic<Number>,就可以在调用该方法时传入任意类型的数据。修改后正常执行,如下:上限通配符的定义
类/接口< ? extends 实参类型>
要求该泛型的类型只能是 实参类型或者 实参类型的子类
下限通配符要求该泛型的类型只能是 实参类型或者 实参类型的父类类型
下限通配符的定义
类/接口< ? super 实参类型>
泛型是JDK1.5引进的,在这之前是没有泛型的,但是,泛型代码能够很好地和之前的代码兼容,那么因为,泛型的信息只存在于代码的编译器,在进入JVM之前,与泛型相关的信息会被擦除,这就是泛型擦除。
初见端倪
运行以下代码,我们可以发现,我们定义的存放String和Integer类型的两个ArrayList集合intList和StrList是同一个ArrayList,那是因为在运行时我们传入的类型变量String和Integer都被擦除了。
无限制擦除
无限制擦除,就是无论你给我的泛型类型是什么,Java虚拟机会都将你的类型擦除掉变为Object类型
有限制擦除
编译后都将其擦除变为上限类型
擦除方法中类型定义的参数
同样的泛型擦除也可以作用域泛型方法中
Integer类型的Generic类,经过编译后成员方法
method()以及getElement()的返回值类型被擦除变为泛型的上限Number.
桥接方法
首先我们定义一个Info泛型接口包含method()方法,然后InfoImpl实现Info接口并指定泛型接口的泛型类型为Integer并重写接口中的method()方法,通过反射查看编译后InfoImpl实现类中的方法有哪些及其返回值类型。
运行以上main方法后,我们可以发现InfoImpl实现类中出现了两个
名称一样但返回值类型和参数列表的类型都不同的method方法。这是为什么呢,明明在实现类中只有重写了一个method方法?其实其中
返回值类型和参数类型为Object的method()是Java虚拟机会帮我们添加的,该方法就是一个桥接方法。因为我们定义的Integer类型的泛型接口Info编译后会被擦除变为Object类型,而我们的实现类InfoImpl为了保证我们对接口实现的规范和约束,所以Java 虚拟机会帮我们在实现类中生成一个返回值类型和参数类型为Object的method()方法,从而
保持接口和类的实现关系。
创建一个泛型数组有以下规范:
ArrayList<String> [] listArray;,但是不能直接创建带泛型的数组对象,如
ArrayList<String> [] listArray = new ArrayList<String>[2];因此,我们可以通过以下方式创建泛型数组:
以上就是对Java中泛型知识点的总结,掌握好泛型的使用,可以提高我们代码的重用率也通过消除了强制的类型转换可以提高我们程序的安全性。
最近我整理了整套 《JAVA核心知识点总结》,说实话 ,作为一名Java程序员,不论你需不需要面试都应该好好看下这份资料。拿到手总是不亏的~我的不少粉丝也因此拿到腾讯字节快手等公司的Offer
进 【 Java进阶之路群】,找管理员获取哦-!
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70000430/viewspace-2782141/ ,如需转载,请注明出处,否则将追究法律责任。