Java-泛型机制详解
1: 提出背景
Java集合(Collection)中元素的类型是多种多样的。例如,有些集合中的元素是Byte类型的,而有些则可能是String类型的,等等。Java允许程序员构建一个元素类型为Object的Collection,其中的元素可以是任何类型在[Java SE](https://baike.baidu.com/item/Java SE/4662159?fromModule=lemma_inlink) 1.5之前,没有泛型(Generics)的情况下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要作显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以在预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。因此,为了解决这一问题,J2SE 1.5引入泛型也是自然而然的了。
2: 泛型简介
泛型概念
? Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制
,该机制允许程序员在编译时
检测到非法的类型
。泛型的本质是参数化类型
,也就是说所操作的数据类型被指定为一个参数。
泛型作用
-
第一是泛化
? 可以用T代表任意类型Java语言中引入泛型是一个较大的功能增强不仅语言、类型系统和编译器有了较大的变化,以支持泛型,而且类库也进行了大翻修,所以许多重要的类,比如集合框架,都已经成为泛型化的了,这带来了很多好处。
-
第二是类型安全
? 泛型的一个主要目标就是提高Java程序的类型安全,使用泛型可以使编译器知道变量的类型限制,进而可以在更高程度上验证类型假设。如果不用泛型,则必须使用强制类型转换,而强制类型转换不安全,在运行期可能发生ClassCast Exception异常,如果使用泛型,则会在编译期就能发现该错误。
-
第三是消除强制类型转换
泛型可以消除源代码中的许多强制类型转换,这样可以使代码更加可读,并减少出错的机会。
-
第四是向后兼容
? 支持泛型的Java编译器(例如JDK1.5中的Javac)可以用来编译经过泛型扩充的Java程序(Generics Java程序),但是现有的没有使用泛型扩充的Java程序仍然可以用这些编译器来编译
3:泛型的基本使用
泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法。下面一部分例子参考与菜鸟教程网站(https://www.runoob.com/java/java-generics.html)
泛型类
泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数,这些类被称为参数化的类或参数化的类型。
例子:
- 简单泛型类
/**
*
* @param <T> 此处是标识符号,T是type简称
*/
public class MyBox<T> {
/**
* t的类型有T指定,创建该对象的时候,指定的类型
*/
private T t;
public void add(T t) {
this.t = t;
}
public T get() {
return t;
}
}
public class MainTest {
public static void main(String[] args) {
MyBox<String> stringMyBox = new MyBox<>();
stringMyBox.add("dddd");
System.out.println(stringMyBox.get());
MyBox<Integer> integerMyBox = new MyBox<>();
integerMyBox.add(23);
System.out.println(integerMyBox.get());
}
}
- 多元泛型类
/**
*
* @param <K> 变量key的类型,由外部指定
* @param <V> 变量value的类型,由外部指定
*/
public class MyMap<K,V> {
private K key;
private V value;
public K getKey() {
return this.key;
}
public V getValue() {
return this.value;
}
public void setKey(K key) {
this.key = key;
}
public void setValue(V value ){
this.value = value;
}
}
public class MainTest {
public static void main(String[] args) {
MyMap<String, String> stringStringMyMap = new MyMap<>();
stringStringMyMap.setKey("key1");
stringStringMyMap.setValue("key2");
System.out.println(stringStringMyMap.getKey()+"="+ stringStringMyMap.getValue());
MyMap<String, Integer> stringIntegerMyMap = new MyMap<>();
stringIntegerMyMap.setKey("key1");
stringIntegerMyMap.setValue(1234);
System.out.println(stringIntegerMyMap.getKey()+"="+ stringIntegerMyMap.getValue());
}
}
泛型接口
泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中
例子:
当实现泛型接口的类,没有传入泛型实参的时候:
/**
* 定义一个泛型接口
* @param <T>
*/
public interface Generator<T> {
public T getNext();
}
/**
* 没有传入参数实参时,与泛型类的定义相同,在声明类的时候,需要将泛型的声明也一起加到类中
* @param <T>
*/
class GoodsGenerator<T> implements Generator<T> {
@Override
public T getNext() {
return null;
}
}
当实现泛型接口的类,传入泛型实参时候:
/**
* 传入泛型实参后,那么这个实现类里面的所有的参数都指定的类型,不能更改
* @param <Params>
*/
public class MyGoodsGenerator<Pa