在Java5后推出了泛型,使我们在编译期间操作集合或类时更加的安全,更方便代码的阅读,而让身为编译性语言的Java提供动态性的反射技术,更是在框架开发中大行其道,从而让Java活起来,下面看一下在使用泛型和反射需要注意和了解的事情。
Java中的泛型是类型可擦除的
Java中的泛型在编译期间是有效的,在运行期间将会被删除,也就是所有泛型参数类型在编译后都会被擦除掉,例如:
public static void test(List<Integer>testParameter) {
}
public static void test(List<String>testParameter) {
}
上面两个方法,除了泛型不同外,其他部分完全相同,但是这两个方法算不算重载呢?答案是不算。因为这段代码在编译之后,泛型类型是会被擦除的,即编译之后这两个泛型都会变成List<E>
,所以他们的参数类型是相同的,因此这两个方法不是重载,也不能在同一个类中出现。可以通过getClass()
方法,获取他们的类型,以作比较。
不能初始化泛型参数和数据
先看一段代码
class Test<T> {
//编译不通过
private T t = new T();
//编译不通过
private T[] tArray = new T[5];
//编译通过
private List<T> list = new ArrayList<T>();
}
Java语言是一门强类型,编译型的安全语言,它需要确保运行期间的稳定性和安全性,所以在编译时会对我们所声明的类型进行严格的检查。以上代码在编译期间,编译器会获取T类型,编译后泛型被擦除,所以new T()
和new T[5]
都会出现编译错误。
强制声明泛型的实际类型
在使用泛型时,在大多数情况下应该声明泛型的类型,例如,集合是用来存放User对象的,就应该使用 List<User>
来声明,这样可以尽量减少类型的转换,若指使用List而不声明泛型,则集合等同于 List<Object>
。
泛型不存在继承
先看代码
public static void main(String[] args) {
// 编译不通过
List<Object>list = new ArrayList<Integer>();
// or
List<Object> list = new ArrayList<Double>();
}
以上代码的业务逻辑为:有一组元素,不确定元素是整形还是浮点型,但是在确定后创建对应的泛型实现类。但是这种代码的声明方式是错误的,因为泛型之间不存在继承关系,以上逻辑可以换一种实现方式:
public static void main(String[] args) {
//Number 为Integer 和 Double 的公共父类
List<Number>numberList = new ArrayList<Number>();
//使用通配符,代表实际类型是Number类型的子类
List< ? extends Number> numberList2 = new ArrayList<Integer>();
//or
List< ? extends Number> numberList3 = new ArrayList<Double>();
}
不同场景使用不同的通配符
泛型中支持通配符,可以单独使用?来表示任意类型,也可以使用extends关键字表示传入参数是某一个类或接口的子类;也可以使用super关键字表示接受某一个类型的父类,extends和super的写法一样,extends是限定上界,super为限定下界。
建议使用顺序List<T>
List<?>
List<Object>
三者都可以容纳所有对象,但是使用时有差别,因为List<T>
是确定某一类型,List<?>
表示任意类型,List<Object>
中全部表示为Object类型,这与不使用泛型无异。List是可读可写的,因为已经明确了,而其他两者在读取时需要进行向下转型,造成了不必要的转型。
使用多重边界限定
如果有业务需求:收钱时需要是公司的员工(继承User),同时亦需要具备收银员的功能(实现Cashier),此时在泛型中可以通过以下代码实现:
public static <T extends User&Cashier> void CollectMoney(T t){
}
文章整理自:查看出处;