1 什么是泛型?通俗的讲就是可以代替其它类型,
为什么要使用泛型?
类似于抽象归类,每个类型都可以使用同一个方法或者类或者接口来调用。
//设置Integer类型的点坐标
class IntegerPoint{
private Integer x ; // 表示X坐标
private Integer y ; // 表示Y坐标
public void setX(Integer x){
this.x = x ;
}
public void setY(Integer y){
this.y = y ;
}
public Integer getX(){
return this.x ;
}
public Integer getY(){
return this.y ;
}
}
//设置Float类型的点坐标
class FloatPoint{
private Float x ; // 表示X坐标
private Float y ; // 表示Y坐标
public void setX(Float x){
this.x = x ;
}
public void setY(Float y){
this.y = y ;
}
public Float getX(){
return this.x ;
}
public Float getY(){
return this.y ;
}
}
每个类都是单独的具体的类型,分别只能传入Interger或这Float 型,两个类型的代码基本一样,那么进一步归类
class Object{
private Object x ; // 表示X坐标
private Object y ; // 表示Y坐标
public void setX(Object x){
this.x = x ;
}
public void setY(Object y){
this.y = y ;
}
public Object getX(){
return this.x ;
}
public Object getY(){
return this.y ;
}
由于Interger和Float都派生自Object,这样可以传入不同的类型,取出时在强制类型转换。
ObjectPoint integerPoint = new ObjectPoint();
integerPoint.setX(new Integer(100));
Integer integerX=(Integer)integerPoint.getX();
但是如果由于不知道传入什么类型,取出的时候强制类型转为其它类型会怎么样?编译时不会报错,但是运行时会报类型转换错误。
那有没有一种办法在编译阶段,即能合并成同一个,又能在编译时检查出来传进去类型不对呢?当然,这就是泛型。
//定义
class Point<T>{// 此处可以随便写标识符号
private T x ;
private T y ;
public void setX(T x){//作为参数
this.x = x ;
}
public void setY(T y){
this.y = y ;
}
public T getX(){//作为返回值
return this.x ;
}
public T getY(){
return this.y ;
}
};
//IntegerPoint使用
Point<Integer> p = new Point<Integer>() ;
p.setX(new Integer(100)) ;
System.out.println(p.getX());
//FloatPoint使用
Point<Float> p = new Point<Float>() ;
p.setX(new Float(100.12f)) ;
System.out.println(p.getX());
可以看到在构造泛型类的实例的时候,先传入相应的类型,取得时候就不会出错了。
总结两点优势:
(1) 使用泛型不用再强制类型转换
(2) 生成实例时传入什么类型,p.setX(new Integer(100))如果传入其它类型会报错。
2 多泛型变量定义及字母规范
(1)多泛型变量定义
如果要传多个泛型变量怎么办?
class MorePoint<T,U,A,B,C>{
}
如上所示,字母大写,并用逗号隔开,并不一定非要用T才可以当作泛型,其它大写字母也可以。
class MorePoint<T,U> {
private T x;
private T y;
private U name;
public void setX(T x) {
this.x = x;
}
public T getX() {
return this.x;
}
…………
public void setName(U name){
this.name = name;
}
public U getName() {
return this.name;
}
}
//使用
MorePoint<Integer,String> morePoint = new MorePoint<Integer, String>();
morePoint.setName("harvic");
Log.d(TAG, "morPont.getName:" + morePoint.getName());
使用的时候就可以传多种类型的变量了。
(2)字母规范
- E — Element,常用在java Collection里,如:List<E>,Iterator<E>,Set<E>
- K,V — Key,Value,代表Map的键值对
- N — Number,数字
- T — Type,类型,如String,Integer等等
字母没有特别的定义,只是为了提高可读性。
3 泛型接口定义
上面讲的是泛型类的定义,下面主要介绍泛型接口定义。
interface Info<T>{ // 在接口上定义泛型
public T getVar() ; // 定义抽象方法,抽象方法的返回值就是泛型类型
public void setVar(T x);
}
定义时也是在接口名后加尖括号。
有两种使用方法,第一种是非泛型类实现该接口,第二种是泛型类实现该接口
class InfoImpl implements Info<String>{ // 定义泛型接口的子类
private String var ; // 定义属性
public InfoImpl(String var){ // 通过构造方法设置属性内容
this.setVar(var) ;
}
@Override
public void setVar(String var){
this.var = var ;
}
@Override
public String getVar(){
return this.var ;
}
}
public class GenericsDemo24{
public void main(String arsg[]){
InfoImpl i = new InfoImpl("harvic");
System.out.println(i.getVar()) ;
}
};
非泛型类只能在实现泛型接口时就填充类型,如果要在使用时才填充类型就需要使用泛型类了。
class InfoImpl<T> implements Info<T>{ // 定义泛型接口的子类
private T var ; // 定义属性
public InfoImpl(T var){ // 通过构造方法设置属性内容
this.setVar(var) ;
}
@Override
public void setVar(T var){
this.var = var ;
}
@Override
public T getVar(){
return this.var ;
}
}
public class GenericsDemo24{
public void main(String arsg[]){
InfoImpl<String> i = new InfoImpl<String>("harvic");
System.out.println(i.getVar()) ;
}
};
泛型类实现泛型接口,只需要在生成实例的时候传入类型,通过构造函数将类型传给接口。
4 泛型函数的使用
如果我们想单独在一个方法中使用泛型,可以有两种定义方法:
public class StaticFans {
//静态函数
public static <T> void StaticMethod(T a){
Log.d("harvic","StaticMethod: "+a.toString());
}
//普通函数
public <T> void OtherMethod(T a){
Log.d("harvic","OtherMethod: "+a.toString());
}
}
分别是静态函数和普通函数,使用静态方法,与以往方法的唯一不同点就是在返回值前加上<T>来表示泛型变量。其它没什么区别。
调用方法:
StaticFans.StaticMethod("adfdsa");//使用方法一
StaticFans.<String>StaticMethod("adfdsa");//使用方法二
第一种是隐式调用,直接省去传值类型,不建议使用,不利于维护;第二种是在函数前加上要填充的类型,建议使用;
上面是无返回值的情况,如果有返回值,那么该怎么写?
public class StaticFans {
//静态函数
public static <T> T StaticMethod(T a){
Log.d("harvic","StaticMethod: "+a.toString());
return a;
}
//普通函数
public <T> T OtherMethod(T a){
Log.d("harvic","OtherMethod: "+a.toString());
return a;
}
}
直接用T来代替void就可以啦。
如果是返回数组呢?
//定义
public static <T> T[] fun1(T...arg){ // 接收可变参数
return arg ; // 返回泛型数组
}
//使用
public static void main(String args[]){
Integer i[] = fun1(1,2,3,4,5,6) ;
Integer[] result = fun1(i) ;
}
和返回String[]类型的数组一样,返回泛型数组,就在函数前加上T[ ] 即可。
总结一下,一是什么是泛型?为什么要使用泛型?泛型的使用可以预先填充类型,防止运行时由于类型不匹配导致程序崩溃。
二是泛型类的使用,泛型类后加尖括号<T>,如果多泛型参数,就用逗号隔开<T,U,P>并大写;
三是泛型接口的使用,使用方法也是在接口加尖括号<T>,如果要调用接口,可能有非泛型和泛型类实现接口,泛型类通过构造函数,将泛型填充到接口,非泛型只能在实现接口时填充。
四是泛型函数的使用,在返回类型前加尖括号<T>,声明要填充的类型,也可以省略但不建议这么用。