JavaSE-Adventure (III): 泛型程序设计
CONTENTS
概述
泛型概念
泛型本质是参数化类型,也就是说所操作的数据类型被指定为一个参数(type parameter)。
这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。
参数化类型其实可以这样理解,假设我们有一个容器,容器需要存储什么类型的数据呢?可以通过定义:
class Holder <T> {
T property;
}
Holder<String> holder = new Holder<>();
这样就把所存储的数据,的类型,实现了参数化,在实例化容器的时候,用参数的形势限定容器内存储的数据类型是什么。
泛型的提出背景
泛型出现的主要原因是为了创建容器类,能够用来指定容器要持有什么类型的对象,由编译器来保证类型的正确性。
想象一下,如果没有泛型(Generics)的情况下,容器可以通过持有Object 来保证容器可以持有任意类型的对象,来实现参数的“任意化”,“任意化”带来的缺点是要作显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以在预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。
泛型的作用
-
泛化
可以用T
代表任意类型。Java语言中引入泛型是一个较大的功能增强,不仅语言、类型系统和编译器有了较大的变化,以支持泛型,而且类库也进行了大翻修,所以许多重要的类,比如集合框架,都已经成为泛型化的了,这带来了很多好处。 -
类型安全
泛型的一个主要目标就是提高Java程序的类型安全,使用泛型可以使编译器知道变量的类型限制,进而可以在更高程度上验证类型假设。如果不用泛型,则必须使用强制类型转换,而强制类型转换不安全,在运行期可能发生ClassCast Exception异常,如果使用泛型,则会在编译期就能发现该错误。(将错误提前到编译期) -
消除强制类型转换
泛型可以消除源代码中的许多强制类型转换,这样可以使代码更加可读,并减少出错的机会。 -
向后兼容
支持泛型的Java编译器可以用来编译经过泛型扩充的Java程序(Generics Java程序),但是现有的没有使用泛型扩充的Java程序仍然可以用这些编译器来编译。
使用泛型
泛型类
public class Holder<T> {
private T item;
public void setItem(T item) {
this.item = item;
}
public T getItem() {
return item;
}
}
泛型接口
public interface Generator<T> {
T next();
}
始终不确定泛型的类型,直到创建对象时,确定泛型的类型
public class GenericGenerator<T> implements Generator<T> {
@Override
public T next() {
return null;
}
}
定义类时确定泛型的类型
public class StringGenerator implements Generator<String> {
@Override
public String next() {
return null;
}
}
泛型方法
泛型方法可以存在于非泛型类或是泛型类。
public <T> T print(T param){
System.out.println(param.getClass());
System.out.println(param);
return param;
}
- public 与 返回值中间
<T>
可以理解为声明此方法为泛型方法。只有声明了<T>
的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法,如:
// 不属于泛型方法
public T next() {
return null;
}
-
<T>
表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T -
方法定义中 返回值与参数类型都能定义为泛型
T
参数化类型
Type Parameter / Type Arugument
介绍另一个概念 Type Parameter 和 Type Argument, 翻译过来就是形参和实参
方法定义中的参数变量被称为形参,实际传递给方法的值称为实参。如下:
public void method(Integer int) {
}
obj.method(10);
例子中的int就是形参,10 则是实参
应用到泛型中就是Type Parameter / Type Argument
class Box<T> {
}
Box<Integer> box = new Box<>();
Box是一个泛型类,T为其形参。使用时我们指定Integer作为泛型类Box的实参。
常用形参:
E:表示 Element,即元素,运用在集合中
K:表示 Key,即键
V:表示 Value,即值
N:表示 Number,即数值类型
T:表示 Type,即 Java 类型
?:表示不确定的 Java 类型
Parameterized Type / Raw Type
首先理解一个概念:
虚拟机没有泛型类型对象,所有对象都是普通类。无论何时定义泛型,都自动提供了一个原始类型(Raw Type)。原始类型就是直接删去类名后的泛型类型。
示例:
public class A<T> {
}
A<String> classA = new A