前言:无论何时,相信自己。
相关文章:
1、《夯实JAVA基本之一 —— 泛型详解(1)》
2、《夯实JAVA基本之一 —— 泛型详解(2)》
ArrayList<String> strList = new ArrayList<String>(); ArrayList<Integer> intList = new ArrayList<Integer>(); ArrayList<Double> doubleList = new ArrayList<Double>();大家对ArrayList很熟悉,这里构造了三个List,分别盛装String、Integer和Double;这就是ArrayList的过人之处:即各种类型的变量都可以组装成对应的List,而不必针对每个类型分别实现一个构建ArrayList的类。这里可能看不懂,开篇总是困难的,下面看看如果没有泛型的话,我们要怎么做;
//设置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 ;
}
}那现在有个问题:大家有没有发现,他们除了变量类型不一样,一个是Integer一个是Float以外,其它并没有什么区别!那我们能不能合并成一个呢?class ObjectPoint{
private Object x ;
private Object 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 ;
}
}即全部都用Object来代替所有的子类;ObjectPoint integerPoint = new ObjectPoint(); integerPoint.setX(new Integer(100)); Integer integerX=(Integer)integerPoint.getX();在设置的时候,使用new Integer(100)来新建一个Integer
integerPoint.setX(new Integer(100));然后在取值的时候,进行强制转换:
Integer integerX=(Integer)integerPoint.getX();由于我们设置的时候,是设置的Integer,所以在取值的时候,强制转换是不会出错的。
ObjectPoint floatPoint = new ObjectPoint(); floatPoint.setX(new Float(100.12f)); Float floatX = (Float)floatPoint.getX();但问题来了:注意,注意,我们这里使用了强制转换,我们这里setX()和getX()写得很近,所以我们明确的知道我们传进去的是Float类型,那如果我们记错了呢?
ObjectPoint floatPoint = new ObjectPoint(); floatPoint.setX(new Float(100.12f)); String floatX = (String)floatPoint.getX();不会!!!我们问题的关键在于这句:
String floatX = (String)floatPoint.getX();强制转换时,会不会出错。因为编译器也不知道你传进去的是什么,而floatPoint.getX()返回的类型是Object,所以编译时,将Object强转成String是成立的。必然不会报错。
//定义
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()); 先看看运行结果:从结果中可以看到,我们实现了开篇中IntegerPoint类和FloatPoint类的效果。下面来看看泛型是怎么定义及使用的吧。
(1)、定义泛型:Point<T>
首先,大家可以看到Point<T>,即在类名后面加一个尖括号,括号里是一个大写字母。这里写的是T,其实这个字母可以是任何大写字母,大家这里先记着,可以是任何大写字母,意义是相同的。
(2)类中使用泛型
这个T表示派生自Object类的任何类,比如String,Integer,Double等等。这里要注意的是,T一定是派生于Object类的。为方便起见,大家可以在这里把T当成String,即String在类中怎么用,那T在类中就可以怎么用!所以下面的:定义变量,作为返回值,作为参数传入的定义就很容易理解了。
//定义变量
private T x ;
//作为返回值
public T getX(){
return x ;
}
//作为参数
public void setX(T x){
this.x = x ;
} (3)使用泛型类//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());首先,是构造一个实例:
Point<String> p = new Point<String>() ;这里与普通构造类实例的不同之点在于,普通类构造函数是这样的:Point p = new Point() ;
//IntegerPoint使用 Point<Integer> p = new Point<Integer>() ; //FloatPoint使用 Point<Float> p = new Point<Float>() ;尖括号中,你传进去的是什么,T就代表什么类型。这就是泛型的最大作用,我们只需要考虑逻辑实现,就能拿给各种类来用。
public class ArrayList<E>{
…………
}看到了吧,跟我们的Point实现是一样的,这也就是为什么ArrayList能够盛装各种类型的主要原因。//使用Object作为返回值,要强制转换成指定类型 Float floatX = (Float)floatPoint.getX(); //使用泛型时,不用强制转换,直接出来就是String System.out.println(p.getVar());(2)、在settVar()时如果传入类型不对,编译时会报错
可以看到,当我们构造时使用的是String,而在setVar时,传进去Integer类型时,就会报错。而不是像Object实现方式一样,在运行时才会报强制转换错误。
class MorePoint<T,U>{
}也就是在原来的T后面用逗号隔开,写上其它的任意大写字母即可。想加几个就加几个,比如我们想加五个泛型变量,那应该是这样的:class MorePoint<T,U,A,B,C>{
}举个粟子,我们在Point上再另加一个字段name,也用泛型来表示,那要怎么做?代码如下: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());从上面的代码中,可以明显看出,就是在新添加的泛型变量U用法与T是一样的。class Point<T>{
…………
}当然不是的!!!!任意一个大写字母都可以。他们的意义是完全相同的,但为了提高可读性,大家还是用有意义的字母比较好,一般来讲,在不同的情境下使用的字母意义如下:在接口上定义泛型与在类中定义泛型是一样的,代码如下:
interface Info<T>{ // 在接口上定义泛型
public T getVar() ; // 定义抽象方法,抽象方法的返回值就是泛型类型
public void setVar(T x);
} 与泛型类的定义一样,也是在接口名后加尖括号;
(1)、使用方法一:非泛型类
但是在使用的时候,就出现问题了,我们先看看下面这个使用方法:
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()) ;
}
};首先,先看InfoImpl的定义:class InfoImpl implements Info<String>{
…………
}要清楚的一点是InfoImpl不是一个泛型类!因为他类名后没有<T>!public class GenericsDemo24{
public void main(String arsg[]){
InfoImpl i = new InfoImpl("harvic");
System.out.println(i.getVar()) ;
}
};(2)、使用方法二:泛型类在方法一中,我们在类中直接把Info<T>接口给填充好了,但我们的类,是可以构造成泛型类的,那我们利用泛型类来构造填充泛型接口会是怎样呢?
interface Info<T>{ // 在接口上定义泛型
public T getVar() ; // 定义抽象方法,抽象方法的返回值就是泛型类型
public void setVar(T var);
}
class InfoImpl<T> implements Info<T>{ // 定义泛型接口的子类
private T var ; // 定义属性
public InfoImpl(T var){ // 通过构造方法设置属性内容
this.setVar(var) ;
}
public void setVar(T var){
this.var = var ;
}
public T getVar(){
return this.var ;
}
}
public class GenericsDemo24{
public static void main(String arsg[]){
InfoImpl<String> i = new InfoImpl<String>("harvic");
System.out.println(i.getVar()) ;
}
};最关键的是构造泛型类的过程:class InfoImpl<T> implements Info<T>{ // 定义泛型接口的子类
private T var ; // 定义属性
public InfoImpl(T var){ // 通过构造方法设置属性内容
this.setVar(var) ;
}
public void setVar(T var){
this.var = var ;
}
public T getVar(){
return this.var ;
}
}在这个类中,我们构造了一个泛型类InfoImpl<T>,然后把泛型变量T传给了Info<T>,这说明接口和泛型类使用的都是同一个泛型变量。public class GenericsDemo24{
public static void main(String arsg[]){
Info<String> i = new InfoImpl<String>("harvic");
System.out.println(i.getVar()) ;
}
};使用泛型类来继承泛型接口的作用就是让用户来定义接口所使用的变量类型,而不是像方法一那样,在类中写死。class InfoImpl<T,K,U> implements Info<U>{ // 定义泛型接口的子类
private U var ;
private T x;
private K y;
public InfoImpl(U var){ // 通过构造方法设置属性内容
this.setVar(var) ;
}
public void setVar(U var){
this.var = var ;
}
public U getVar(){
return this.var ;
}
}在这个例子中,我们在泛型类中定义三个泛型变量T,K,U并且把第三个泛型变量U用来填充接口Info。所以在这个例子中Info所使用的类型就是由U来决定的。public class GenericsDemo24{
public void main(String arsg[]){
InfoImpl<Integer,Double,String> i = new InfoImpl<Integer,Double,String>("harvic");
System.out.println(i.getVar()) ;
}
}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");//使用方法二
//常规方法
StaticFans staticFans = new StaticFans();
staticFans.OtherMethod(new Integer(123));//使用方法一
staticFans.<Integer>OtherMethod(new Integer(123));//使用方法二
结果如下:首先,我们看静态泛型函数的使用方法:
StaticFans.StaticMethod("adfdsa");//使用方法一
StaticFans.<String>StaticMethod("adfdsa");//使用方法二从结果中我们可以看到,这两种方法的结果是完全一样的,但他们还有些区别的,区别如下:StaticFans staticFans = new StaticFans(); staticFans.OtherMethod(new Integer(123));//使用方法一 staticFans.<Integer>OtherMethod(new Integer(123));//使用方法二可以看到,与平常一样,先创建类的实例,然后调用泛型函数。
public static <T> List<T> parseArray(String response,Class<T> object){
List<T> modelList = JSON.parseArray(response, object);
return modelList;
}函数返回值是List<T>类型。至于传入参数Class<T> object的意义,我们下面会讲。这里也就是想通过这个例子来告诉大家,泛型变量其实跟String,Integer,Double等等的类的使用上没有任何区别,T只是一个符号,可以代表String,Integer,Double……这些类的符号,在泛型函数使用时,直接把T看到String,Integer,Double……中的任一个来写代码就可以了。唯一不同的是,要在函数定义的中在返回值前加上<T>标识泛型;public static List<SuccessModel> parseArray(String response){
List<SuccessModel> modelList = JSON.parseArray(response, SuccessModel.class);
return modelList;
} 其中SuccessModel是自定义的解析类,代码如下,其实大家不用管SuccessModel的定义,只考虑上面的那段代码就行了。写出来SuccessModel的代码,只是不想大家感到迷惑,其实,这里只是fastJson的基本用法而已。public class SuccessModel {
private boolean success;
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
} 那现在,我们把下面这句组装成一个泛型函数要怎么来做呢?public static List<SuccessModel> parseArray(String response){
List<SuccessModel> modelList = JSON.parseArray(response, SuccessModel.class);
return modelList;
}首先,我们应该把SuccessModel单独抽出来做为泛型变量,但parseArray()中用到的SuccessModel.class要怎么弄呢?public static <T> List<T> parseArray(String response,Class<T> object){
List<T> modelList = JSON.parseArray(response, object);
return modelList;
}注意到,我们用的Class<T> object来传递类的class对象,即我们上面提到的SuccessModel.class。public final class Class<T> implements Serializable {
…………
}通过Class<T>来加载泛型的Class对象的问题就讲完了,下面来看看泛型数组的使用方法吧。//定义
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) ;
} 我们先看看 定义时的代码:public static <T> T[] fun1(T...arg){ // 接收可变参数
return arg ; // 返回泛型数组
} 首先,定义了一个静态函数,然后定义返回值为T[],参数为接收的T类型的可变长参数。如果有同学对T...arg的用法不了解,可以去找下JAVA 可变长参数方面的知识。如果本文有帮到你,记得加关注哦
本文所涉及源码下载地址:http://download.csdn.net/detail/harvic880925/9275047
请大家尊重原创者版权,转载请标明出处哦: http://blog.csdn.net/harvic880925/article/details/49872903 谢谢。
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文:http://blog.csdn.net/harvic880925/article/details/49872903