在平常的开发中,咱们会看到别人的框架不少地方会使用到泛型,泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操做的数据类型被指定为一个参数。这种参数类型能够用在类、接口和方法的建立中,分别称为泛型类、泛型接口、泛型方法。泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。本篇博客咱们就来详细解析一下泛型的知识。java
使用泛型有什么好处呢?首先咱们先看一个例子,假设咱们有两个类,代码以下:数组
#StringClass public class StringClass { private String x ; private String y ; public String getY() { return y; } public void setY(String y) { this.y = y; } public String getX() { return x; } public void setX(String x) { this.x = x; } }
#IntClass public class IntClass { private int x ; private int y ; public int getY() { return y; } public void setY(int y) { this.y = y; } public int getX() { return x; } public void setX(int x) { this.x = x; } }
观察上面两个类StringClass 和IntClass,他们除了变量类型不同,一个是String一个是int之外,其它并无什么区别!那咱们能不能合并成一个呢?经过泛型就能够解决,首先看一下泛型的类是怎么定义的:app
public class ObjClass<T> { private T x ; private T y ; public T getX() { return x; } public void setX(T x) { this.x = x; } public T getY() { return y; } public void setY(T y) { this.y = y; } }
那么这时候上面的两个类就能够经过泛型的设置,相应生成框架
ObjClass<String> stringClass = new ObjClass<String>(); stringClass.setX("haha"); ObjClass<Integer> intClass = new ObjClass<Integer>(); intClass.setX(100); Log.d("yyy", "stringClass:" + stringClass.getX() + ",intClass:" + intClass.getX());
从结果中能够看到,咱们经过泛型实现了开篇中StringClass类和IntClass类的效果。ide
接下来介绍泛型如何定义及使用:
1.首先须要定义泛型:ObjClass
ObjClass ,即在类名后面加一个尖括号,括号里是一个大写字母。这里写的是T,其实这个字母能够是任何大写字母,不管使用哪一个字母,意义都是相同的。函数
2.在类中使用泛型
这个T表示派生自Object类的任何类,好比String,Integer,Double等等。这里要注意的是,T必定是派生于Object类的。this
private T x ; private T y ; public T getX() { return x; } public void setX(T x) { this.x = x; } public T getY() { return y; } public void setY(T y) { this.y = y; }
3.使用泛型类
泛型类的使用代码以下:spa
ObjClass<String> stringClass = new ObjClass<String>(); stringClass.setX("haha"); ObjClass<Integer> intClass = new ObjClass<Integer>(); intClass.setX(100);
首先,须要构造一个实例:.net
ObjClass<String> stringClass = new ObjClass<String>();
泛型类的构造则须要在类名后添加上,即一对尖括号,中间写上要传入的类型。
由于咱们构造时,是这样的:ObjClass,因此在使用的时候也要在ObjClass后加上类型来定义T表明的意义。code
尖括号中,你传进去的是什么,T就表明什么类型。这就是泛型的最大做用,咱们只须要考虑逻辑实现,就能拿给各类类来用。
1.多泛型变量定义
咱们不止能够在类中设置一个泛型变量T,还能够声明多个泛型变量,写法以下:
public class ObjClass<T,U>
也就是在原来的T后面用逗号隔开,写上其它的任意大写字母便可,若是还有多个,依然使用逗号分隔开便可,则咱们前面定义的泛型类就会变成下面这样:
public class ObjClass<T,U> { private T x ; private U y ; public T getX() { return x; } public void setX(T x) { this.x = x; } public U getY() { return y; } public void setY(U y) { this.y = y; } }
ObjClass<String,Integer> stringClass = new ObjClass<String,Integer>(); stringClass.setX("haha"); stringClass.setY(100);
从上面的代码中,能够明显看出,就是在新添加的泛型变量U用法与T是同样的。
2.泛型的字母规范
虽然在类中声明泛型任意字母均可以,但为了可读性,最好遵循如下的规范:
E — Element,经常使用在java Collection里,如: List<E>,Iterator<E>,Set<E> K,V — Key,Value,表明Map的键值对 N — Number,数字 T — Type,类型,如String,Integer等等
在接口上定义泛型与在类中定义泛型是同样的,代码以下:
interface MsgClass<T> { public T getMsg() ; public void setMsg(T x); }
咱们能够利用泛型类来构造填充泛型接口
public class Message<T,U> implements MsgClass<T>{ private T msg; @Override public T getMsg() { return msg; } @Override public void setMsg(T msg) { this.msg = msg; } }
在这个类中,咱们构造了一个泛型类Message,而后把泛型变量T传给了MsgClass,这说明接口和泛型类使用的都是同一个泛型变量。
咱们还能够构造一个多个泛型变量的类,并继承自MsgClass接口:
public class Message<T,U> implements MsgClass<T>{ private U name; private T msg; @Override public T getMsg() { return msg; } @Override public void setMsg(T msg) { this.msg = msg; } public U getName() { return name; } public void setName(U name) { this.name = name; } }
咱们不但能够在类声明中使用泛型,还能够在函数声明中也使用泛型,使用以下:
public class ObjClass { //静态函数 public static <T> void StaticMethod(T a) { } //普通函数 public <T> void OrgnicMethod(T a) { } }
上面分别是静态泛型函数和常规泛型函数的定义方法,与以往方法的惟一不一样点就是在返回值前加上来表示泛型变量。
不管哪一种泛型方法都有两种使用方法:
//静态方法 ObjClass.StaticMethod("adfdsa");//使用方法一 ObjClass.<String>StaticMethod("adfdsa");//使用方法二 //常规方法 ObjClass objClass = new ObjClass(); objClass.OrgnicMethod(new Integer(111));//使用方法一 objClass.<Integer>OrgnicMethod(new Integer(111));//使用方法二
方法一,隐式传递了T的类型,这种隐式的传递方式,代码不利于阅读和维护。由于从外观根本看不出来你调用的是一个泛型函数。
方法二,例如上面例子中,将T赋值为Integer类型,这样OrgnicMethod(T a)传递过来的参数若是不是Integer那么编译器就会报错。
固然泛型函数的返回值也可使用泛型表示:
public static <T> List<T> parseArray(String response,Class<T> object){ List<T> modelList = JSON.parseArray(response, object); return modelList; }
函数返回值是List类型。和void的泛型函数不一样,有返回值的泛型函数要在函数定义的中在返回值前加上标识泛型;还要说明的是,上面中,使用Class传递泛型类Class对象
泛型一样能够用来定义在数组上
//定义 public static <T> T[] fun1(T...msg){ // 接收可变参数 return msg ; // 返回泛型数组 } //使用 public static void main(String args[]){ Integer i[] = fun1(8,9,8,44) ; Integer[] result = fun1(i) ; }
定义了一个静态函数,而后定义返回值为T[],参数为接收的T类型的可变长参数。
在开发中对象的引用传递(向上向下传递)是最多见的,可是,在泛型的操做中,在进行引用传递的时候泛型类型必须匹配才能够传递,不然不能传递。
例如,以下没有进行泛型类型匹配,一个是String,一个是Object类型。
class Info<T>{ private T var ; // 定义泛型变量 public void setVar(T var){ this.var = var ; } public T getVar(){ return this.var ; } public String toString(){ return this.var.toString() ; } }; public class demo1 { public static void main(String args[]) { // 使用String为泛型类型 Info<String> i = new Info<String>(); i.setVar("ABCD"); //把String泛型类型的i对象传递给Object泛型类型的temp。 fun(i); } // 接收Object泛型类型的Info对象 public static void fun(Info<Object> temp) { System.out.println("内容:" + temp); } }
编译发生错误。
Exception in thread "main" java.lang.Error: Unresolved compilation problem: The method fun(Info<Object>) in the type demo1 is not applicable for the arguments (Info<String>) at Thread1.demo1.main(demo1.java:18)
泛型对象进行引用传递的时候,类型必须一致,若是非要传递,则能够将fun方法中Info参数的泛型取消掉(变成 void fun(Info temp))。、
以上确实改进了功能,可是彷佛不是很稳当,毕竟以前指定过泛型。
以上程序在fun()方法中使用"Info<?>"
的代码形式,表示可使用任意的泛型类型对象,这样的话fun()方法定义就合理了,可是使用以上方法也有须要注意的地方,
即:若是使用“?“接收泛型对象的时候,则不能设置被泛型指定的内容。
class Info<T>{ private T var ; public void setVar(T var){ this.var = var ; } public T getVar(){ return this.var ; } public String toString(){ return this.var.toString() ; } }; public class GenericsDemo{ public static void main(String args[]){ Info<String> i = new Info<String>() ; i.setVar("ABCD") ; fun(i) ; } public static void fun(Info<?> temp){ System.out.println("内容:" + temp) ; } };
若是使用”?“意味着能够接收任意的内容,可是此内容没法直接使得用”?“修饰的泛型的对象进行修改。以下就会出问题:
class Info<T>{ private T var ; public void setVar(T var){ this.var = var ; } public T getVar(){ return this.var ; } public String toString(){ return this.var.toString() ; } }; public class demo1{ public static void main(String args[]){ Info<?> i = new Info<String>() ; i.setVar("ABCD") ; } };
运行结果:
Exception in thread "main" java.lang.Error: Unresolved compilation problem: The method setVar(capture#1-of ?) in the type Info<capture#1-of ?> is not applicable for the arguments (String) at Thread1.demo1.main(demo1.java:17)
在使用”?“只能接收,不能修改。
class Info<T>{ private T var ; public void setVar(T var){ this.var = var ; } public T getVar(){ return this.var ; } public String toString(){ return this.var.toString() ; } }; public class GenericsDemo{ public static void main(String args[]){ Info<Integer> i1 = new Info<Integer>() ; Info<Float> i2 = new Info<Float>() ; i1.setVar(30) ; i2.setVar(30.1f) ; fun(i1) ; fun(i2) ; } public static void fun(Info<? extends Number> temp){ // 只能接收Number及其Number的子类 System.out.print(temp + "、") ; } };
运行成功。可是,若是传入的泛型类型为String的话就不行,由于String不是Number子类。
在类中使用泛型上限。
class Info<T extends Number>{ // 此处泛型只能是数字类型 private T var ; public void setVar(T var){ this.var = var ; } public T getVar(){ return this.var ; } public String toString(){ return this.var.toString() ; } }; public class demo1{ public static void main(String args[]){ Info<Integer> i1 = new Info<Integer>() ; // 声明Integer的泛型对象 } };
若是在使用Info的时候设置成String类型,则编译的时候将会出现错误(String不是Number子类)
注意:利用<? extends Number>
定义的变量,只可取其中的值,不可修改
缘由以下:
由于Info的类型为 Info
<? super XXX>
表示填充为任意XXX的父类
class Info<T>{ private T var ; public void setVar(T var){ this.var = var ; } public T getVar(){ return this.var ; } public String toString(){ return this.var.toString() ; } }; public class GenericsDemo21{ public static void main(String args[]){ Info<String> i1 = new Info<String>() ; // Info<Object> i2 = new Info<Object>() ; // i1.setVar("hello") ; i2.setVar(new Object()) ; fun(i1) ; fun(i2) ; } public static void fun(Info<? super String> temp){ // 只能接收String或Object类型的泛型,String类的父类只有Object类 System.out.print(temp + "、") ; } };
Object类和String类都是String的父类,全部运行成功,可是若是此时用Integer则会出错,由于integer并非String父类。
注意:使用super通配符:能存不能取
如何理解呢?假设有3个类,继承关系以下:
class CEO extends Manager { } class Manager extends Employee { } class Employee { }
而后书写以下代码:
List<? super Manager> list; list = new ArrayList<Employee>(); //存 list.add(new Employee()); //编译错误 list.add(new Manager()); list.add(new CEO());
为何而list.add(new Employee());是错误的?
由于list里item的类型是
List<Employee> list = new ArrayList<Employee>(); list.add(new Manager()); list.add(new CEO());
在这里,正由于Manager和CEO都是Employee的子类,在传进去list.add()后,会被强制转换为Employee!
如今回过头来看这个:
List<? super Manager> list; list = new ArrayList<Employee>(); //存 list.add(new Employee()); //编译错误 list.add(new Manager()); list.add(new CEO());
编译器没法肯定<? super Manager>
的具体类型,但惟一能够肯定的是Manager()、CEO()确定是<? super Manager>
的子类,因此确定是能够add进去的。但Employee不必定是<? super Manager>
的子类,因此不能肯定,不能肯定的,确定是不容许的,因此会报编译错误。
最后强调一下,List<? super Manager>
list取出的只能是Object 类型,这里虽然看起来是能取的,但取出来一个Object类型,是毫无心义的。因此才有了“super通配符:能存不能取”的结论。
总结
1)使用?能够接收任意泛型对象。
2)泛型的上限:?extends 类型(能取不能存)。
3)泛型的下限:?super 类型? super 通配符(能存不能取)。