所谓的泛型,就是变量类型的参数化。泛型是java1.5中引入的一个重要特征,通过引入泛型,可以使编译时类型安全,运行时更少抛出ClassCastException的可能。提到参数化,就会想到定义方法是由形参,然后调用该方法传入实参。泛型的变量类型的参数化就是将原来传入的具体的类型参数化,这就像方法中的变量参数。使用泛型的时候如果不提供参数类型,即泛型类没有参数化,系统会警告,此时类型为Object。
为什么使用泛型
使用泛型的典型例子,是在集合中的泛型使用。
在使用泛型前,存入集合中的元素可以是任何类型的,当从集合中取出时,所有的元素都是Object类型,需要进行向下的强制类型转换,转换到特定的类型。
List<Integer> myIntList = new LinkedList<Integer>(); // 1'
myIntList.add(new Integer(0)); // 2'
Integer x = myIntList.iterator().next(); // 3'
第一行的List类型说明,编译器会为我们检查类型的正确性。这样,代码的可读性和健壮性也会增强。
上述代码中<>包含的是形式类型参数(formal type parameters),与一般的类型一样,可以在整个类的声明中使用。当类被调用的时候,此时会传入具体的实际类型参数(actual type argument)。如上述例子中的List<Integer>
,那么所有的E将会被Integer代替。
注: 泛型类型参数只能被类或接口类型赋值,不能被原生数据类型(byte,short,int,long,float,double,char,boolean等8种)赋值,原生数据类型需要使用对应的包装类(Byte,Short,Integer,Long,Float,Double,Character,Boolean)。
-
形式类型参数(formal type parameters)命名:
尽量使用单个的大写字母(多个泛型类型会加上数字,如T1,T2),容器集合使用E,代表element,map中用K-keys,V-values。 -
特性:泛型只会在编译阶段有效。栗子如下:
List<String> stringArrayList = new ArrayList<String>(); List<Integer> integerArrayList = new ArrayList<Integer>(); Class classStringArrayList = stringArrayList.getClass(); Class classIntegerArrayList = integerArrayList.getClass(); if(classStringArrayList.equals(classIntegerArrayList)){ Log.d("泛型测试","类型相同"); }
通过上面的例子可以证明,在编译之后程序会采取去泛型化的措施。也就是说Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。
总结:泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。
泛型的应用
三种方式:泛型类,泛型接口,泛型方法。
泛型类
1.1 一个普通的泛型类
public class FXParent<T1, T2> {
private T1 t1;
private T2 t2;
public T1 getT1() {
return t1;
}
public void setT1(T1 t1) {
this.t1 = t1;
}
public T2 getT2() {
return t2;
}
public void setT2(T2 t2) {
this.t2 = t2;
}
}
new一个parent泛型类 ,T1,T2为类型形参,可以接收任何类型,创建的时候传入Integer,String限定接收类型。
FXParent<Integer, String> fxParent = new FXParent<>();
1.2 继承泛型类
public class FXChild<T1, T2, T3> extends FXParent<T1, T2> {
private T3 t3;
public T3 getT3() {
return t3;
}
public void setT3(T3 t3) {
this.t3 = t3;
}
}
泛型接口
2.1 一个泛型接口
/**
* 作者 Superman
* 日期 2018/11/22 10:03.
* 文件 FanXingPractice
* 描述 泛型接口
*/
public interface FXParentInterface<T1, T2> {
void setData1(T1 t1);
void setData2(T2 t2);
T1 getData1();
T2 getData2();
}
2.1 实现泛型接口
/**
* 作者 Superman
* 日期 2018/11/22 9:55.
* 文件 FanXingPractice
* 描述 继承泛型类别 子类
*/
public class FXChild<T1, T2, T3> extends FXParent<T1, T2> implements FXParentInterface<T1, T2> {
private T3 t3;
public T3 getT3() {
return t3;
}
public void setT3(T3 t3) {
this.t3 = t3;
}
@Override
public void setData1(T1 t1) {
}
@Override
public void setData2(T2 t2) {
}
@Override
public T1 getData1() {
return getT1();
}
@Override
public T2 getData2() {
return getT2();
}
}
当实现泛型接口,却没有传入泛型实参的时候,在声明类时,需要将泛型的声明也加到类之中去。
如果不声明就会编译报错:
/ * 即:cclass FXTest<T1, T2> implements FXParentInterface<T1, T2>
* 如果不声明泛型,如:class FXTest implements FXParentInterface<T1,T2>,编译器会报错:*/
泛型方法
3.1 泛型方法创建
/*
*<T>必不可少,表示这是一个泛型方法,并且声明了一个泛型T
* 这个T可以出现在这个泛型方法的任意位置.
* 泛型的数量也可以为任意多个
*/
public static <T> T dowork(T t) {
return t;
}
类型通配符(用?表示)
泛型的通配符:当不知道用何种类型来接收的时候,就可以用?。**此处’?’是类型实参,而不是类型形参 。再直白点的意思就是,此处的?和Number、String、Integer一样都是一种实际的类型,可以把?看成所有类型的父类。是一种真实的类型。**可以解决当具体类型不确定的时候,这个通配符就是 ? ;当操作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用 ? 通配符来表未知类型!
@Override
public void onClick(View view) {
FXTongpeifu<Integer> test1 = new FXTongpeifu<Integer>(123);
showKey(test1);
}
});
/**
*
*/
public void showKey(FXTongpeifu<?> test) {
Log.e("泛型通配符测试:", "" + test.getKey());
}
泛型的上限和下限
在使用泛型的时候,我们还可以为传入的泛型类型实参进行上下边界的限制,如:类型实参只准传入某种类型的父类或某种类型的子类。
/**
*泛型的上限
* 传入的类型必须为指定类型的子类
*
* @param test
*/
public void showKey2(FXTongpeifu<? extends Number> test) {
Log.e("泛型通配符测试:", "" + test.getKey());
}
/**
*泛型的下限
* 传入的类型必须为指定类型的父类
*
* @param test
*/
public void showKey2(FXTongpeifu<? super Number> test) {
Log.e("泛型通配符测试:", "" + test.getKey());
}
后续学习补充中。。
参考博客,文章:
https://blog.csdn.net/s10461/article/details/53941091
https://blog.csdn.net/dreamzuora/article/details/79647960
http://www.cnblogs.com/mengdd/archive/2013/01/21/2869778.html