-
泛型基本概念
-
概念
菜鸟教程
java 泛型是JDK1.5中引入的一个新特性,泛型提供了编译时类型安全监测机制,该机制允许程序员在编译时监测到非法的类型,本质是参数化类型百度百科
泛型是程序设计语言的一种特性。允许程序员在强类型程序设计语言中编写代码时定义一些可变部分,那些部分在使用前必须作出指明。各种程序设计语言和其编译器、运行环境对泛型的支持均不一样。将类型参数化以达到代码复用提高软件开发工作效率的一种数据类型。 -
作用
1. 提高安全性 — 将运行期的错误转换到编译器,尤其是在类型安全的问题上,当我们要操作的对象类型不符合泛型的要求时,就是编译错误,便于程序员在编译期就可以发现错误。
2. 避免强转 — 比如我们在使用List时, 如果我们不使用泛型, 当从List中取出元素时, 其类型会是默认的Object, 我们必须将其向下转型为String才能使用,而使用泛型,就可以保证存入和取出的都是String类型, 不必在进行cast了,使代码具有更好的安全性和可读性。
-
-
泛型的简单使用
- 泛型类
public class pair<T> {
/**
*泛型类可以看做是普通类的工厂
*
**/
private T first;
private T second;
public pair(){
first=null;
second=null;
}
public pair(T first,T second) {
this.first=first;
this.second=second;
}
public T getFirst() {
return first;
}
public void setFirst(T first) {
this.first = first;
}
public T getSecond() {
return second;
}
public void setSecond(T second) {
this.second = second;
}
}
使用要点:
1.类型参数(比如这里的T)可以随便写为任意标识,常见的有T,K,E,V等等
2.在实例化泛型类时,必须指定T的具体类型,指定的类型参数只能是类类型,不能是基本类型
3.构造函数中可以省略类型参数,省略的类型参数可以从指定的具体类型参数中推断得出
ArrayList<String> list = new ArrayList<>();
4.泛型类可以看成是普通类的工厂
- 泛型方法
public <T> T genericMethod(Class<T> tClass)throws InstantiationException ,
IllegalAccessException{
T instance = tClass.newInstance();
return instance;
}
使用要点:
1.public 与 返回值中间非常重要,可以理解为声明此方法为泛型方法。
2.只有声明了的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法,表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T
3.静态方法与泛型 — 静态方法无法访问类型定义的泛型,即无法使用定义泛型类时的类型参数,需要重新再静态泛型方法上声明
public class StaticGenerator<T> {
....
....
/**
* 如果在类中定义使用泛型的静态方法,需要添加额外的泛型声明(将这个方法定义成泛型方法)
* 即使静态方法要使用泛型类中已经声明过的泛型也不可以。
* 如:public static void show(T t){..},此时编译器会提示错误信息:
"StaticGenerator cannot be refrenced from static context"
*/
public static <T> void show(T t){
}
}
-
通配符问题
有时候,类和方法需要对类型变量加以约束,这时候可以使用通配符实现这一点- 上限
为泛型添加上边界,即传入的类型实参必须是指定类型或指定类型的子类。使用extends指定上限通配符
为什么使用extends?而不用implements?
限定类型可以是类,也可以是接口,选择extends的原因是更接近子类的概念
- 上限
/*
* 类型变量的限定
* T 绑定的可以是类也可以是接口(更符合子类的概念)
* 当绑定多个类型变量时,用&隔开
* 可以绑定多个接口类型,但是类只能有一个,且放在第一位,类似继承中的类单继承,接口多继承
*
**/
public static <T extends Comparable<T>>T min(T[] a){
if(a.length==0||a==null) {
return null;
}
T minNum=a[0];
for(int i=0;i<a.length;i++) {
if(minNum.compareTo(a[i])>0){
minNum=a[i];
}
}
return minNum;
}
- 下限
和上限通配符类似,下限通配符使用super关键字实现:
public static void printMsg(Generic<? super Integer> generic) {
System.out.println(generic.getKey());
}
一个类型变量或通配符可以有多个限定
T extends Comparable & Serializable
限定类型间用&分割开,逗号用来分割类型变量
在java的继承中,可以根据需要拥有多个接口的超类型,但限定中最多只有一个类,且必须放到第一位
3. 类型擦除
Java 的泛型只在编译阶段有效,编译过程中正确检验泛型结果后,会将泛型相关信息擦除,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法,即泛型信息不回进入运行时阶段,无论何时定义一个泛型类型,都自动提供一个相应的原始类型,原始类型的名字就是删除类型参数后的泛型类型名,擦除类型变量,并且替换为限定类型(无限定的变量用Object),有多个限定的,就使用第一个限定类型
public class pair<T> {
/**
*泛型类可以看做是普通类的工厂
*
**/
private T first;
private T second;
public pair(){
first=null;
second=null;
}
public pair(T first,T second) {
this.first=first;
this.second=second;
}
public T getFirst() {
return first;
}
public void setFirst(T first) {
this.first = first;
}
public T getSecond() {
return second;
}
public void setSecond(T second) {
this.second = second;
}
}
//没有限定,类型擦除后的原始类型
public class pair {
private Object first;
private Object second;
public pair(){
first=null;
second=null;
}
public pair(Object first,Object second) {
this.first=first;
this.second=second;
}
public Object getFirst() {
return first;
}
public void setFirst(Object first) {
this.first = first;
}
public Object getSecond() {
return second;
}
public void setSecond(Object second) {
this.second = second;
}
}
public class pair<T extends Comparable & Serializable> {
private T first;
private T second;
public pair(){
first=null;
second=null;
}
public pair(T first,T second) {
this.first=first;
this.second=second;
}
public T getFirst() {
return first;
}
public void setFirst(T first) {
this.first = first;
}
public T getSecond() {
return second;
}
public void setSecond(T second) {
this.second = second;
}
}
他的原始类型是
public class pair implements Serializable{
private Comparable first;
private Comparable second;
public pair(){
first=null;
second=null;
}
public pair(Comparable first,Comparable second) {
this.first=first;
this.second=second;
}
public Comparable getFirst() {
return first;
}
public void setFirst(Comparable first) {
this.first = first;
}
public Comparable getSecond() {
return second;
}
public void setSecond(Comparable second) {
this.second = second;
}
}
5.泛型的约束和局限性
- 不能用基本类型实例化类型参数----不能用类型参数代替基本类型
- 运行时类型查询只适用于原始类型(区分:和类型擦除中的限定类型不一样)
- 不能创建参数化类型的数组
- 不能实例化类型变量
- 泛型类的静态上下文中类型变量无效–泛型类中的静态域或静态方法不能引用泛型变量
- 不能抛出或捕获泛型类的实例–catch子句不能使用类型参数
- 可以消除对受查异常的检查