Java1.5发行版中增加了泛型。
声明中具有一个或者多个类型参数的类或者接口,就是泛型类或者接口。泛型类和接口统称为泛型。
每种泛型定义一组参数化的类型,构成格式为:先是类或者接口的名称,接着用尖括号(<>)把对应于泛型形式类型参数的实际类型参数列表括起来。例如,List
每个泛型有定义一个原生态类型,即不带任何实际参数的泛型名称。例如,与List
如果要使用泛型,但不确定或者不关心实际的类型参数,可以使用一个问号代替。
用泛型编程时,会遇到许多编译器警告:非受检强制转化警告、非受检方法调用警告、非受检普通数组创建警告,以及非受检转换警告。
有许多非受检警告很容易消除;有些警告比较难以消除。如果无法消除警告,同时可以证明引起警告的代码是类型安全的,可以用一个@SuppressWarnings("unchecked")注解来禁止这条警告。
@SuppressWarnings注解可以用在任何粒度的级别中,从单独的局部变量声明到整个类都可以。应该始终在尽可能小的范围中使用@SuppressWarnings注解。
每当使用@SuppressWarnings注解时,都要添加一条注释,说明为什么这么做是安全的。
数组与泛型相比,有两个重要的不同点。
首先数组是协变的。如果Sub为Super的子类型,那么数组类型Sub[]就是Super[]的子类型。而泛型是不可变的。
如下面这段代码是合法的:
Object[] objects = new Integer[4];
objects[0] = "I don't fit it";
而下面这段代码是不合法的:
其实无论哪种方法,都不能将String类型放在Integer容器中,但是利用数组,只能在运行时发现错误;利用列表,则可以在编译时发现错误。
数组与泛型之间的第二大区别在于,数组是具体化的,因此数组会在运行时才知道并检查它们的元素类型约束。而泛型则是通过擦除来实现的。因此泛型只在编译时强化它们的类型信息,并在运行时丢弃它们的元素类型信息。擦除就是使泛型可以与没有使用泛型的代码随意进行互用(见第二十三条)。
由于上述的区别,因此数组和泛型不能很好地混合使用。数组是斜边且可以具体化的;泛型是可变的且可以被擦除的。
一般来说,将集合声明参数化,以及使用JDK所提供的泛型和泛型方法,这些都不太困难。编写自己的泛型会比较困难一些,但值得花时间去学习如何编写。
将之间编写过的Stack改写成泛型的:
package JavaDay5_29;
import java.util.Arrays;
import java.util.EmptyStackException;
/**
* @author [email protected]
* @date 18-5-29 下午8:27
*/
public class Stack {
private E[] items;
private int size = 0;
private static final int DEFAULT_INITAL_CAPACITY = 16;
public Stack() {
items = new E[DEFAULT_INITAL_CAPACITY];
}
public void push(E o) {
ensureCapacity();
items[size++] = o;
}
public E pop() {
if(size == 0) {
throw new EmptyStackException();
}
E result = items[--size];
items[size] = null;//消除过期引用
return result;
}
/**
* 确保容量足够且自动扩增
*/
private void ensureCapacity() {
if(items.length == size) {
items = Arrays.copyOf(items, 2 * size + 1);
}
}
}
但在构造函数中会有警告。
可将构造函数更改成:
public Stack() {
items = (E[]) new Object()[DEFAULT_INITAL_CAPACITY];
}
此时还是会有警告,但此种用法确实是合理的,可加上@SuppressWarnings("unchecked")注解即可。
总而言之,使用泛型比使用需要在客户端代码中进行转换的类型来得更加安全,也更加容易。
就如同类可以从泛型中受益一般,方法也一样。静态工具方法尤其适合泛型化。
下面为一个泛型方法的栗子:
public static Set union(Set s1, Set s2) {
Set result = new HashSet<>();
result.addAll(s2);
return result;
}
union方法的局限性在于,三个集合的类型(两个输入参数和一个返回参数)必须全部相同。利用有限制的通配符类型,可以使这个方法变得更加灵活(见第二十八条)。
泛型方法的一个显著特征是:无需明确指定类型参数的值,不像调用泛型构造器的时候是必须指定。
总而言之,泛型方法就像泛型一样,使用起来比要求客户端转换输入参数并返回值的方法来得更加安全,也更加容易。
Set extends E>可以存放E和E的子类。
Set super E>可以存放E和E的父类。
使用通配符可以让API更加灵活。
略。(本章内容多而杂,最近时间不够,以后重读补上)
总结:本章主要讲解了泛型的知识和泛型的用法,以及如何正确高效地使用泛型。共勉。