共计 1567 个字符,预计需要花费 4 分钟才能阅读完成。
定义:声明中具有一个或者多个类型参数 (type parameter) 的类或者接口,就是泛型类或者接口。泛型类和接口统称为泛型(generic type)。
每种泛型定义一组类型形参 (formal type parameters),这些类型形参有时也被简称为类型参数(type parameter),例如对于泛型(generic type)List<E> 而言,List<String> 就是一个参数化的类型(parameterized type),String 就是对应于类型形参(formal type parameters) 的类型实参(actual type parameter)。
每个泛型定义一个原生类型(raw type),即不带任何类型参数的类型名称,例如,与 List<String> 对应的原生类型是 List。原生类型就像从类型声明中删除了所有泛型信息一样。实际上原生类型 List 与 Java 平台在有泛型之前的接口类型 List 完全一样。
容器类使用泛型的好处:
安全性:在对参数化类型的容器中放入了错误即不匹配的类型的时候,编译器将会强制性进行错误提示。
便利性:当从容器中取出元素的时候不用自己手动将 Object 转换为元素的实际类型了,编译器将隐式地进行自动转换。
表述性:带有类型实参的泛型即参数化类型,可以让人看到实参就知道里面的元素 E 都是什么类型。
所以,不应该使用原生类型的原因如下:
虽然使用原生类型是合法的,但不提倡这样做,因为如果使用原生类型,就失掉了泛型在安全性和表述性方面的所有优势;安全性:比如我们可能会不小心把一个 java.util.Date 实例错误地放进一个原本包含 java.sql.Date 实例的集合当中,虽然在编译期不会出现任何错误,但在运行期一旦尝试类型转换就会发生 ClassCastException,而泛型原本就是为了避免这种问题而出现的;表述性: 不像带有类型实参的泛型即参数化类型那样,让人看到实参就知道里面的元素 E 都是什么类型。
泛型的子类型化的原则:List<String> 类型是原生类型 List 的一个子类型,而不是参数化类型 List<Object> 的子类型
List、List<?>、List<Object> 的区别
List,即原始类型,其引用变量可以接受任何对应 List<E> 的参数化类型,包括 List<?>,并且可以添加任意类型的元素。但其缺点在于不安全性、不便利性、不表述性(不应该使用原生类型的原因)。
List<?>,即通配符类型,其引用变量,同样可以接受任何对应 List<E> 的参数化类型,包括 List,但不能添加任何元素,保证了安全性和表述性。但不具有表述性,从中取出的元素时 Object 类型,要通过手动转换才能得到原本的类型。
List<Object>,即实际类型参数为 Object 的参数化类型,其引用变量可以接受 List,可以添加元素,但不能接受除了其本身外的任何参数化类型(泛型的子类型化原则)。
引用变量的类型
名称
可以接受的类型
能否添加元素
安全性
便利性
表述性
List
原始类型
任何对应 List<E> 的参数化类型,包括 List<?>
可以添加任意类型的元素
无
无
无
List<?>
通配符类型
以接受任何对应 List<E> 的参数化类型,包括 List
不能添加任何元素
有
无
有
List<Object>
实际类型参数为 Object 的参数化类型
仅可以接受 List 和其本身类型
可以添加任意类型元素
有
有
有
可以看到相比参数化类型的 List<Object>,List<?> 缺点在于不能添加任何元素并且不具有便利性,如果这无法满足功能要求可以考虑使用泛型方法和有边界的通配符。
根据 The Java™ Tutorials,原生类型对象可以被赋给参数化类型 (包括有界无界通配符参数化类型),同样,参数化类型(包括有界无界通配符参数化类型) 对象也能被赋给原生类型