您的位置:首页 > 编程语言 > Java开发

java基础加强--自定义泛型方法及其应用

2014-02-16 23:27 746 查看
Java的泛型是从C++的模板函数借鉴过来的,但是Java的泛型没有C++的泛型强大,

这是由于Java虚拟机设计的原因;但是它还是尽量去模仿C++的泛型。
那么C++里面是怎么解决泛型的?

如下函数的结构很相似,仅类型不同:
int add(int x,int y) {
return x+y;
}
float add(float x,float y) {
return x+y;
}
double add(double x,double y) {
return x+y;
}

C++用模板函数解决,只写一个通用的方法,它可以适应各种类型,示意代码如下:
template<class T>   //T就代表类型不祥,可以是任意类型。
T add(T x,T y) {
return (T) (x+y);
}

Java中的泛型类型(或者泛型)类似于 C++ 中的模板。
但是这种相似性仅限于表面,Java 语言中的泛型基本上完全是在编译器中实现,用于编译器执行类型检查和类型推断,然后生成普通的非泛型的字节码,这种实现技术称为擦除(erasure)(编译器使用泛型类型信息保证类型安全,然后在生成字节码之前将其清除)。这是因为扩展虚拟机指令集来支持泛型被认为是无法接受的,这会为 Java 厂商升级其 JVM 造成难以逾越的障碍。所以就不要完全追求C++的模板了,但是可以模仿一下。



“+”操作符没有为参数类型T,T定义。也就是说+操作符不支持操作两个T类型。

不是任意类型都支持加法操作。上面的方法确实没办法实现C++的功能。

用于放置泛型的类型参数的尖括号应出现在方法的其他所有修饰符之后和在方法的返回类型之前,也就是紧邻返回值之前。

按照惯例,类型参数通常用单个大写字母表示。

----------------------------



--------------------

交换数组中的两个元素的位置的泛型方法语法定义如下:

private static <T> void swap(T[] a,int i,int j){

T temp = a[i];

a[i] = a[j];

a[j] = temp;

}

//或用一个面试题讲:把一个数组中的元素的顺序颠倒一下

只有引用类型才能作为泛型方法的实际参数,swap(new int[]{1,2,3,4,5},3,4);语句会报告编译错误。



只有引用类型才能作为泛型方法的实际参数,对于add方法,使用基本类型的数据进行测试没有问题,这是因为自动装箱和拆箱了。
swap(new int[3],3.5);语句会报告编译错误,这是因为编译器不会对new int[3]中的int自动拆箱和装箱了,
因为new int[3]本身已经是对象了,你想要的有可能就是int数组呢?它装箱岂不弄巧成拙了。
-------------------

除了在应用泛型时可以使用extends限定符,在定义泛型时也可以使用extends限定符。(在定义泛型的时候,也可以限定类型)
例如,Class.getAnnotation()方法的定义。



getAnnotation方法的返回值是A类型。<A extends Annotation>在声明A这个类型,限定了A这个类型必须是Annotation或Annotation的子类类型。因此A可以是在一个范围内的任意类型。

参数Class(A)就是声明接收A这种类型的Class

-----------------

并且可以用&来指定多个边界,如<V extends Serializable & cloneable> void method(){}

说明V这个引用类型必须实现了Serializable 和 cloneable接口这就限定了V的类型的范围。

---------------

也可以用类型变量表示异常,称为参数化的异常,可以用于方法的throws列表中,但是不能用于catch子句中。

用下面的代码说明对异常如何采用泛型:

private static <T extends Exception> void sayHello() throws T{

try{

}catch(Exception e){

throw (T)e;

}

}

这个应用是不多的。

------------

在泛型中可以同时有多个类型参数,在定义它们的尖括号中用逗号分,例如:

public static <K,V> V getValue(K key) { return map.get(key);}

--------------

自定义泛型方法的练习与类型推断总结

泛型方法的练习题:

1、编写一个泛型方法,自动将Object类型的对象转换成其他类型。

public static <T> T autoConvertType(Object obj){
return (T)obj;
}


2、定义一个方法,可以将任意类型的数组中的所有元素填充为相应类型的某个对象。

public static <T> void fillArray(T[] arr,T element){
for(int i = 0; i < arr.length; i++)
arr[i] = element;
}


3、采用自定泛型方法的方式打印出任意参数化类型的集合中的所有内容。

public static <T> void printCollection(Collection<T> collection,T col){
//和原来的程序有些区别的。原来的程序:public static void printCollection(Collection<?> collection){}
for(Object obj : collection){
System.out.println(obj);
}
collection.add(col);//这里是可以添加元素的,原来的程序就不可以。
}


4、定义一个方法,把任意参数类型的集合中的数据安全地复制到相应类型的数组中。

这个需要使用泛型方法进行定义,如果使用如下形式:static void copy(Collection a, Object[] b);就会有可能出现A类型的数据复制进B类型的数组中的情况。

使用泛型方法的定义形式为:static <T> void copy(Collection<T> a,T[] b);就没有这个问题。

5、定义一个方法,把任意参数类型的一个数组中的数据安全地复制到相应类型的另一个数组中。

copy1(new Vector<String>(),new String[10]);

copy2(new Date[10],new String[10]);//类型推断,T为Date数组和String数组的组合类型--》Object数组

//copy1(new Vector<Date>(),new String[10])这个时候就不类型推断了。

//为Vector<>使用了泛型,为Vector的泛型指向了一个Date,那么就说明了那个T就是个Date了。

//在对Vector<>进行参数化的时候,指明为Date,就说明了T就是个Date。那么String类型就对不上了。所以会报错。

private static<T> void copy2(T[] dest, T[] src) {

}

private static<T> void copy1(Collection<T> dest, T[] src) {

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐