JAVA的泛型
起源:
试想,如果只写一个单独的排序方法,但可以针对任意类型的对象进行排序,那该多爽啊,JAVA的泛型正是为此类需求而生。简单来说,泛型方法即只声明一个而却可以当作多个方法来使用,泛型类即只声明一个而却可以当作多个类型来使用。
另外,泛型提供了编译期的类型安全性检查以便帮助开发人员捕获到编译期的异常。
泛型方法:
详细来说,泛型方法就是只声明一个方法,但向其传送不同类型的参数,编译器会依据传递的参数类型,分别调用与之对应的实际方法。定义泛型方法的规则如下:
- 所有的泛型方法需要在方法的返回类型之前放置由<>包围的类型参数
- 多方法需要设置多个类型参数时,使用逗号分隔,类型参数就是泛型的名字
- 类型参数可以当作方法的返回值类型,也可以当作方法的形参类型
- 泛型方法的内部方法体类似于普通方法的内部方法体,但要注意,类型参数只能是引用类型的,不能是内置数据类型,比如int,double,char等
下面是泛型方法的一个例子:
public class GenericMethodTest { // generic method printArray public static < E > void printArray( E[] inputArray ) { // Display array elements for ( E element : inputArray ){ System.out.printf( "%s ", element ); } System.out.println(); } public static void main( String args[] ) { // Create arrays of Integer, Double and Character Integer[] intArray = { 1, 2, 3, 4, 5 }; Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 }; Character[] charArray = { 'H', 'E', 'L', 'L', 'O' }; System.out.println( "Array integerArray contains:" ); printArray( intArray ); // pass an Integer array System.out.println( "\nArray doubleArray contains:" ); printArray( doubleArray ); // pass a Double array System.out.println( "\nArray characterArray contains:" ); printArray( charArray ); // pass a Character array } }
有些时候,需要限制传递到某个方法中的数据类型,例如,如果一个方法是用来进行数值运算的,那么应该只向其内部传递数值类型的实例或数值类型的子类型的实例。此时,使用边界类型参数即可。
声明一个边界类型参数,首先写出类型参数的名字,后面跟extends关键字,然后再跟上它的上层边界类型,比如:
<T extends Comparable<T>>//上层边界是Comparable接口,即只接收实现了Comparable接口的类型
这里的extends关键字有双重含义,但上层边界是类时,它的意义就是“继承”的意思,当上层边界是接口时,它的意义就是“实现”的意思。
下面是带有类型边界的泛型方法的一个例子:
public class OwenSort { public static <T extends Comparable<T>> void sort(T[] array) { System.out.println("Sub Class should override this method..."); } private static <T extends Comparable<T>> boolean less(T a, T b) { return a.compareTo(b) < 0; } private static <T extends Comparable<T>> void exchange(T[] arr, int i, int j) { T temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } private static <T extends Comparable<T>> void show(T[] arr) { for (int i = 0; i < arr.length; i++) { System.out.println(arr[i].toString() + "\r\n"); } } public static <T extends Comparable<T>> boolean isSorted(T[] arr) { for (int i = 1; i < arr.length; i++) if (less(arr[i], arr[i - 1])) return false; return true; } }
泛型类:
泛型类的声明方式跟普通类的声明大同小异,只需要在类名的后面跟上泛型的类型参数即可。例:
public class Box<T> { private T t; public void add(T t) { this.t = t; } public T get() { return t; } public static void main(String[] args) { Box<Integer> integerBox = new Box<Integer>(); Box<String> stringBox = new Box<String>(); integerBox.add(new Integer(10)); stringBox.add(new String("Hello World")); System.out.printf("Integer Value :%d\n\n", integerBox.get()); System.out.printf("String Value :%s\n", stringBox.get()); } }
针对类内部的成员变量或成员方法,还有另外一种声明方式:
public class StaticParameter { public static Comparable<?> member; public static void test(Comparable<?> x){} }
重要说明:
泛型类,这里的泛型是服务于这个类的,也就是这个类在被实例化时,才会确定泛型的最终实际类型,这个实际类型会被应用到该类中所有使用到泛型类中声明的类型变量。
泛型成员,这里的泛型是服务于类中指定成员的,跟类没有关系,类就是个普通的类,这是类内成员特殊化的一种体现方式,多用于对类内方法,即实现泛型方法。
此时可能会有疑问,既然类都”反省“了,那类中的成员岂不更”反省“了,通常情况下是这样,但【静态】成员是个大大的例外,因为静态成员的初始化机制跟普通成员是完全不同的。
JAVA中的泛型是伪泛型,它是通过编译期间的类型擦除实现的,简单看两个例子:
public static void main(String[] args) { ArrayList arrayList=new ArrayList(); arrayList.add(1); arrayList.add("121"); arrayList.add(new Date()); }
这个例子中,没有为ArrayList指定具体的类型,即ArrayList现在是一个元型(raw type),它可以承载任何对象,即承载Object对象,所以此时可以向其内添加任意的数据类型。
而如果为ArrayList指定一个比如叫Goal的类型,即 new ArrayList<Goal>(),那这个LIST中只能存放Goal类型及其子类型的数据,因为编译期间的泛型擦除是指定类型”上限“为Goal,
即类型只能是Goal及其子类型。
上面提到的【静态】在泛型类中是个例外的原因,无非就是因为静态成员在类初始化(非对象实例化)时就要确定擦除上限,而泛型类只能在进行对象实例化确定最终的数据类型,
如果JAVA不做控制,这就会导致在运行期间,静态成员操作的最终数据类型跟类实例化后的对象中存储的最终数据类型不一致,进而出现运行时异常。因此,在处理泛型时,JAVA采用的
控制策略是先检查代码中的泛型类型,确定擦除上限,然后进行擦除,然后才进行编译。当泛型类中出现了静态成员时,在JAVA进行代码检查时,就会给出错误,这种严格的控制就是为了避免
运行时静态成员与非静态成员最终的数据类型不一致的潜在风险,所以,在泛型类中的静态成员,是不允许使用泛型类的类型变量的。
此时,可能会再有疑问,为什么普通类的静态成员可以使用泛型,因为普通类的静态泛型成员在类初始化时,可以正确擦除出类型上限,如果不指定无非就到达最顶层的Object,
再者,静态成员与非静态成员由于没有使用共同的类型变量,就不会出现最终二者数据类型不相同的风险,所以JAVA是允许这种情况的。
最后一点通,泛型类是为了存储多种数据类型,泛型方法是为了操作多种数据类型,静态反省方法同样也是为了操作多种数据类型,如果是以数据类型的多样化为目标,则采用泛型类,
如果是以操作对象的多样化为目标,则采用泛型方法,最好是静态泛型方法,避免与成员变量共存。
/**************************************外星人乔丹拍板时间**************************************/
技术无止境,入行需谨慎