概述
什么是泛型?
泛型,是JDK5中引入的特性,它提供了编译时类型安全检测机制,该机制允许在编译时检测到非法的类型它的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?
顾名思义,就是将类型由原来的具体的类型参数化,然后在使用/公用时传入具体的类型这种参数类型可以用在类、方法和接口中,分别被称为泛型、泛型方法、泛型接口。
为什么要使用泛型?
一言以蔽之,编译时检测到非法的类型。
场景:给你一个ArrayList,让你求该list中所有元素的和。(注意,该list原则上是要存int类型的数的)
没有泛型之前:
ArrayList arrayList = new ArrayList();arrayList.add(1);arrayList.add(2);arrayList.add(3);//非法输入,但不会报错arrayList.add("3");
求list的元素和的话我会遍历该list,然后依次相加。
发现这个list最后存入了一个String类型的值,当你遍历相加肯定是得不到正确答案的!
使用泛型之后:
ArrayList<Integer> arrayList = new ArrayList<>();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
//非法输入,直接报错
arrayList.add("3");
可以看出在创建list时指定了存储的类Integer,因此在后续添加元素时添加了String就直接报错了,因此求和操作就不会受到影响。
泛型种类
泛型类
基本语法:
示例:
public class Generic<T> {//成员属性T value;public Generic(T value) {this.value = value;}//注意这不是泛型方法T getValue() {return this.value;}
}
注意点:
- 使用时才指定类型,不能是基本数据类型(泛型底层是通过Object实现的,基本类型不继承Object)
- 若不指定类型,则默认为Object
- 指定不同类型后他们实际上依然是该泛型类(即泛型只在编译期间有效,就是是所谓的泛型擦除)
扩展:
对泛型类进行继承时应该注意以下两点:
泛型接口
泛型接口跟泛型类的使用基本上是一样的,在此不再赘述,但实现接口时依然要牢记以下两点:
泛型方法
泛型类和泛型接口都很容易理解,所以本文的重点是泛型方法,相信大家在学习泛型方法过程中会有几个疑问:
- 为啥使用到泛型成员的方法不是泛型方法
- 泛型方法有啥用
先抛出问题,相信看完接下来的案例之后就会恍然大悟。
语法:
可以发现泛型方法的申明要在返回值前加<>,也就是说只有加了这个尖括号的方法才是泛型方法,而上面的泛型类中的示例就不是泛型方法。
解惑:
先来看为什么要有泛型方法:
假如我们定义一个方法,用于返回给定任意类型的参数的String(虽然这是没有意义的),
既然要具有通用性,那么我们会想到用泛型来解决,因此定义了以下的方法:
其实我们定义了一个泛型类,然后里面的方法使用到了泛型,但是事与愿违,这个方法并不能返回任何类型参数的String,因为这个方法不是泛型方法,在创建泛型类时就把它指定成了Integer类型,当我们传入Long类型的参数时编译器就会报错。
接下来我们使用真正的泛型方法:
可以发现不再报错了,但是前面也说了这个方法实际上是没有意义的,因此下面我们定义一个有意义的方法:
给定类的字节码对象,然后返回一个该类的实列
我们会考虑:
- 给定类字节码创建对象肯定就调用字节码对象的newInstance方法
- 既然要通用,那么返回值是不确定的,那直接用Object没问题吧
于是就得出了以下代码:(请忽略类上的< T >)
可以发现能够正常运行,但是细心的你一定会发现调用方法后必须要强转类型!
所以就要使用泛型方法了:(请忽略类上的< T >)
至此关于泛型方法的一些疑惑相信都已经消失了吧!
注意点:
- 泛型方法中的泛型和泛型类的泛型是独立的
- 如果static方法要使用泛型能力,就必须使其成为泛型方法(因为泛型类需要指定类型才能确定)