Java泛型
Java 泛型(generics)是 JDK 5 中引入的一个新特性, 又称参数化类型。
泛型是JVM不认可,编译器认可的,编译器在编译程序的时候就把<E>删除了,而把所有泛型的参数和返回值类型都被改成Object类型。当获取以泛型定义的返回值类型数据的时候,编译器会自动补充造型代码,将返回值类型(造型前为Object类型)向下造型为泛型所指定的实际类型。
泛型指的是参数化类型,就是说所操作的数据类型被指定为一个参数。允许我们使用一个类的时候,可以指定类中属性、方法参数及其方法返回值的类型。可以使程序更加灵活。
泛型如果没有指定类型的话,默认是Object。在使用时指定泛型的实际类型,且实际类型必须是引用类型,不可以用基本类型。如果泛型是包装类时,且传入的元素是基本类型时,则还会有自动拆箱操作。
提出背景
Java集合(Collection)中元素的类型是多种多样的。例如,有些集合中的元素是Byte类型的,而有些则可能是String类型的,等等。Java允许程序员构建一个元素类型为Object的Collection,其中的元素可以是任何类型在Java1.5之前,没有泛型(Generics)的情况下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要作显式的强制类型转换。而这种转换是要求开发者对实际参数类型可以在预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。
泛型作用
第一是泛化。可以用T代表任意类型Java语言中引入泛型是一个较大的功能增强不仅语言、类型系统和编译器有了较大的变化,以支持泛型,而且类库也进行了大翻修,所以许多重要的类,比如集合框架,都已经成为泛型化的了,这带来了很多好处。
第二是类型安全。泛型的一个主要目标就是提高Java程序的类型安全,使用泛型可以使编译器知道变量的类型限制,进而可以在更高程度上验证类型假设。如果不用泛型,则必须使用强制类型转换,而强制类型转换不安全,在运行期可能发生ClassCast Exception异常,如果使用泛型,则会在编译期就能发现该错误。
泛型的使用
<>里面可以是任何大写字母。但为了提高可读性,大家还是用有意义的字母比较好,一般来讲,在不同的情境下使用的字母意义如下:
E — Element,常用在java Collection里,如:List<E>,Iterator<E>,Set<E>
K,V — Key,Value,代表Map的键值对
N — Number,数字
T — Type,类型,如String,Integer等等
<T>
这是最基础的泛型应用,用作类的定义上面,表示接收什么类型的定义。最常见的就是List,Map这种。我们看一下代码
List<Student> list = new ArrayList<Student>();
就像这样,我们指定List的内容是Student对象,如果传入其他的类型,则将会报错。这样的好处在于我们创建对象的时候,就可以指定它的类型,避免传入的类型出错。比如说一个学生数组里,你放入一个桌子对象,这肯定是不行的。
使用泛型除了可以编译的时候提示类型错误,还有一个好处就是不需要强制类型转换。比如说我们获取到List里面的一个元素,如果我们指定了它的泛型,那么取出来的就是指定的泛型类型。
<? extend E>
这个泛型的时候不是在定义类的时候使用的,而是在定义对象的时候使用的。
List<? extends Person> list = new ArrayList<Person>();
这里定义的时候用这个语法,但是new的时候就必须指定具体的类型。 <? extends E> 是 Upper Bound(上限) 的通配符,用来限制元素的类型的上限。意思就是,在这个list中,元素只能是Person以及它的子类。Person的父类不可以添加。
<?>
在说“?”之前先说了一下“<? extends E>”。因为单独一个“?”表示的意思也是很简单,就是“<? extends Object>”的简写而已。也就是说元素可以是任意引用类型。
<? super E>
这个与之前的extends有些相似,但是描述的意思是完全相反的。<? extends E>表示上限,而<? super E>表示的是下限。意思就是说,里面的内容起码是E或者是E的父类。如果赋值的是子类或者其他的类,则会编译不通过。
PECS法则
PECS法则:生产者(Producer)使用extends,消费者(Consumer)使用super
1.生产者
如果你需要一个提供E类型元素的集合,使用泛型通配符<? extends E>。它好比一个生产者,可以提供数据。
2.消费者
如果你需要一个只能装入E类型元素的集合,使用泛型通配符<? super E>。它好比一个消费者,可以消费你提供的数据。
频繁往外读取内容的,适合用上界Extends。经常往里插入的,适合用下界Super。