最近在imooc上面买了一个有关spring源码分析的课程,前几天花了不少时间才把spring源码编译成功,真的太花时间了,这几天就开始了spring的学习,今天在学习课程的时候,老师讲解到了泛型。泛型我在以前确实学过,但是随着时间的推移,逐渐忘记了,今天就想来总结一个泛型的使用。学无止境,好好学习吧,少年,加油!
泛型的好处
(以下内容来自百度百科)
Java语言中引入泛型是一个较大的功能增强。不仅语言、类型系统和编译器有了较大的变化,以支持泛型,而且类库也进行了很大的改动,许多重要的类,比如集合框架,都已经成为泛型化的了。这带来了很多好处:
类型安全
泛型的主要目标是提高Java程序的类型安全。通过知道使用泛型定义的变量的类型限制,编译器可以在非常高的层次上验证类型假设。没有泛型,这些假设就只存在于系统开发人员的头脑中。
通过在变量声明中捕获这一附加的类型信息,泛型允许编译器实施这些附加的类型约束。类型错误就可以在编译时被捕获了,而不是在运行时当作ClassCastException展示出来。将类型检查从运行时挪到编译时有助于Java开发人员更早、更容易地找到错误,并可提高程序的可靠性。
消除强制类型转换
泛型的一个附带好处是,消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。尽管减少强制类型转换可以提高使用泛型类的代码的累赞程度,但是声明泛型变量时却会带来相应的累赞程度。在简单的程序中使用一次泛型变量不会降低代码累赞程度。但是对于多次使用泛型变量的大型程序来说,则可以累积起来降低累赞程度。所以泛型消除了强制类型转换之后,会使得代码加清晰和筒洁。
更高的运行效率
在非泛型编程中,将筒单类型作为Object传递时会引起Boxing(装箱)和Unboxing(拆箱)操作,这两个过程都是具有很大开销的。引入泛型后,就不必进行Boxing和Unboxing操作了,所以运行效率相对较高,特别在对集合操作非常频繁的系统中,这个特点带来的性能提升更加明显。
潜在的性能收益
泛型为较大的优化带来可能。在泛型的初始实现中,编译器将强制类型转换(没有泛型的话,Java系统开发人员会指定这些强制类型转换)插入生成的字节码中。但是更多类型信息可用于编译器这一事实,为未来版本的JVM的优化带来可能。
泛型的实现原理
泛型的实现是靠类型擦除技术 类型擦除是在编译期完成的 也就是在编译期 编译器会将泛型的类型参数都擦除成它的限定类型,如果没有则擦除为object类型之后在获取的时候再强制类型转换为对应的类型。 在运行期间并没有泛型的任何信息,因此也没有优化。
验证泛型的实现原理
为了验证泛型在编译的时候使用了类型擦除技术,在运行期间不会包含任何泛型的信息,我们使用IDEA打开泛型编译后的class文件,就可以进行查看。
//定义泛型类
public class GenericFactory<T> {
T member;
GenericFactory(T member){
this.member = member;
}
public T getMember() {
return member;
}
}
//使用泛型类
public class GenericDemo {
public static void main(String[] args) {
GenericFactory<Integer> integerGenericFactory = new GenericFactory<Integer>(123);
GenericFactory<String > stringGenericFactory = new GenericFactory<String>("cxf");
System.out.println(integerGenericFactory.getMember());
System.out.println(stringGenericFactory.getMember());
}
}
//使用IDEA中控制台,然后使用 javac *.java,编译完成后,我们在打开GenericDemo查看
//我们可以清楚的看到在,编译完成后,泛型已经被擦除了,所以在运行的时候不会有泛型的任何信息
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.generic;
public class GenericDemo {
public GenericDemo() {
}
public static void main(String[] var0) {
GenericFactory var1 = new GenericFactory(123);
GenericFactory var2 = new GenericFactory("cxf");
System.out.println(var1.getMember());
System.out.println((String)var2.getMember());
}
}
泛型的使用
泛型在Java中主要用于泛型接口,泛型类,泛型方法,在下面呢,我也会一一举例。
我们需要注意:泛型不支持基本数据类型
代码实现
/**
* 泛型接口
*/
public interface GenericFactory<T,N> {
T nextObject();
N nextNumber();
}
import java.util.Random;
/**
* 泛型类:实现一
*/
public class RobotFactory implements GenericFactory<String,Integer> {
private Integer[] integers = {1,2,3,4,5,6};
private String[] strings = {"qwe","asd","zxc","rty","fgh","vbn"};
@Override
public String nextObject() {
Random random = new Random();
return strings[random.nextInt(6)];
}
@Override
public Integer nextNumber() {
Random random = new Random();
return integers[random.nextInt(6)];
}
public static void main(String[] args) {
GenericFactory genericFactory = new RobotFactory();
System.out.println(genericFactory.nextNumber());
System.out.println(genericFactory.nextObject());
}
}
/**
* 泛型类:实现二
* 注意:①GenericFactoryDemo<T,N>中T和N的顺序是可以交换的。
* ②GenericFactoryDemo<T,N>与GenericFactory<T,N>泛型的字母必须相同
*/
public class GenericFactoryDemo<T,N> implements GenericFactory<T,N>{
@Override
public T nextObject() {
return null;
}
@Override
public N nextNumber() {
return null;
}
}
/**
* 泛型方法的使用
*/
public class GenericMethod {
/**
* 泛型方法
*/
public static <E> void printElements(E[] elements){
for(E e : elements){
System.out.printf("%s",e);
System.out.print(" ");
}
System.out.println();
}
public static void main(String[] args) {
Integer[] integers = {1,2,3,4};
Double[] doubles = {1.1,1.2,1.3,1.4};
String[] strings = {"qwe","asd","zxc","cxf"};
printElements(integers);
printElements(doubles);
printElements(strings);
}
}
泛型统配符的使用
泛型统配符指的是 ? 这个符号,但是使用统配符虽然好用,但是也存在巨大的坑
public class GenericFactory<T> {
T member;
GenericFactory(T member){
this.member = member;
}
public T getMember() {
return member;
}
}
public class GenericDemo {
/**
* 加法运算
*/
public static void addition(GenericFactory<? super Integer> genericFactory){
int result = 111 + (Integer) genericFactory.getMember();
System.out.println(result);
}
public static void main(String[] args) {
GenericFactory<Integer> integerGenericFactory = new GenericFactory<>(123);
GenericFactory<String> stringGenericFactory = new GenericFactory<>("cxf");
// 条件:public static void addition(GenericFactory<Integer> genericFactory)
// addition(integerGenericFactory);//输出结果为234
// 条件:public static void addition(GenericFactory<?> genericFactory)
// 这个时候下面方法在编译的时候不会报错,但是在运行的时候会抛出ClassCastException
// 所以说 统配符? 其实就相当于潘多拉宝盒,用起来好用,但是一不小心就会抛出异常
// 如何解决这个问题,我们可以给 通配符? 添加相关界限
// addition(stringGenericFactory);
// 条件:public static void addition(GenericFactory<? extends Number> genericFactory)
// 给通配符添加上界限 使用规则是 ? extends xxx
// 意思就是,我们传递过来的类必须是xxx的子类,所以也叫做通配符的上界限
// addition(integerGenericFactory);
// addition(stringGenericFactory);//这个时候就会在编译的时候报错
// 条件:public static void addition(GenericFactory<? super Integer> genericFactory)
// 我们还可以给统配添加下界限 使用规则是 ? super xxx
// 意思就是,我们传递过来的类必须是xxx的父类,所以也叫做通配符的下界限
// addition(integerGenericFactory);
// addition(stringGenericFactory);//这个时候就会在编译的时候报错
}
}
泛型字母的含义
- E-Element : 在集合中使用,因为集合中存放的是元素
- T-Type : Java类
- K-Key : 键
- V-Value : 值
- N-Number : 数值类型