Java泛型浅析

来源:互联网 发布:英国gpa标准加权算法 编辑:程序博客网 时间:2024/06/14 20:01


一句话理解


泛型是一种检查机制,使得在编译时检测出错误,而不是运行时。


优点


加强安全性。


知识要点(详细


以下例子来源:Java语言程序设计(进阶篇)

定义的泛型类会在后续的例子中持续使用


1.定义泛型类&接口

import java.util.ArrayList;//定义泛型类public class GenericStack <E>{ArrayList<E> list = new ArrayList<>();public int getSize(){return list.size();}public E peek(){return list.get(getSize()-1);}public void push(E o){list.add(o);}public E pop(){E o = list.get(getSize()-1);list.remove(getSize()-1);return o;}public boolean isEmpty(){return list.isEmpty();}@Overridepublic String toString() {return "stack:" + list.toString() ;}}
//应用泛型类添加元素,再输出public class GenericDemo {public static void main(String[] args) {// TODO Auto-generated method stubGenericStack<String> stack1 = new GenericStack<>();stack1.push("London");stack1.push("Paris");stack1.push("Berlin");GenericStack<Integer> stack2 = new GenericStack<>();stack2.push(1);stack2.push(2);stack2.push(3);System.out.println(stack1.pop());System.out.println(stack1.pop());System.out.println(stack1.pop());System.out.println();System.out.println(stack2.pop());System.out.println(stack2.pop());System.out.println(stack2.pop());System.out.println();}}

注意这是一个栈类


2.定义泛型方法

package test;public class GenNewDemo {//定义泛型方法public static <E> void print (E[] list){for(int i = 0;i<list.length;i++){System.out.print(list[i]+ " ");System.out.println();}}public static void main(String[] args) {// TODO Auto-generated method stubInteger[] integeres = {1,2,3,4,5};String[] strings = {"London","Paris","New York"};print(integeres);System.out.println();//以下三种输出方式都是正确的print(strings);System.out.println();GenNewDemo.print(strings);System.out.println();GenNewDemo.<String>print(strings);}}


注意:

是可以为静态方法定义泛型类型。


3.受限泛型&非受限泛型

受限泛型:泛型指定为另外一种类型的子类型。eg:<E entends GenericOperation> (GenericOperation是一个自定义类

非受限泛型:假设有一个<E>,它就等同于<E entends Object>


4.原始类型及向后兼容

原始类型:没有指定具体类型的泛型类和泛型接口被称为原始类型。

eg:

GenericStack stack = new GenericStack();//大体等价于GenericStack<Object> stack = new GenericStack<Object>();
像这样不带类型参数的泛型类就是原始类型。

注意:原始类是不安全的。

5.通配泛型重点

通配泛型分类:

(1)非受限通配     

和? exends Object是一样的

(2)受限通配       <? extends T >

表示T或T的一个子类型

(3)下限通配       <? super T >

表示T或T的一个父类型


应用举例:

package test;public class WildCardNeedDemo {//找到stack中的最大数字public static double max(GenericStack<? extends Number> stack){double max = stack.pop().doubleValue();while(!stack.isEmpty()){double value = stack.pop().doubleValue();if(value>max){max = value;}}return max;}public static  void print (GenericStack<Object> stack){while(!stack.isEmpty()){System.out.print(stack.pop() + " ");}}public static void main(String[] args) {// TODO Auto-generated method stubGenericStack<Integer> intStack = new GenericStack<>();intStack.push(1);intStack.push(2);intStack.push(-2);print(intStack);}}


错误分析:虽然Integer是Object的子类型,但是GenericStack<Integer>不是GenericStack<Object>的子类型

修改代码为


运行结果



<? extends T >应用举例:

package test;public class WildCardNeedDemo {//找到stack中的最大数字public static double max(GenericStack<Number> stack){double max = stack.pop().doubleValue();while(!stack.isEmpty()){double value = stack.pop().doubleValue();if(value>max){max = value;}}return max;}public static void main(String[] args) {// TODO Auto-generated method stubGenericStack<Integer> intStack = new GenericStack<>();intStack.push(1);intStack.push(2);intStack.push(-2);System.out.println("The max number is : " + max(intStack));}}


错误分析:虽然Integer是Number的子类型,但是GenericStack<Integer>不是GenericStack<Number>的子类型,所以要用通配泛型去解决问题。


修改代码为

运行结果



<? super T >应用举例:

package test;public class SuperWildCardDemo {public static void main(String[] args) {// TODO Auto-generated method stubGenericStack<String> stack1 = new GenericStack<>();GenericStack<Object> stack2 = new GenericStack<>();stack2.push("Java");stack2.push(2);stack1.push("Sun");add(stack1,stack2);print(stack2);}private static <T> void add(GenericStack<T> stack1, GenericStack<? super T> stack2) {while(!stack1.isEmpty()){stack2.push(stack1.pop());}}public static  void print (GenericStack<?> stack){while(!stack.isEmpty()){System.out.println(stack.pop() + " ");}}}

运行结果



6.消除泛型

泛型在编译时,一旦编译器确认泛型类型是安全使用的,就会将它转换为原始类型。

例如:


确认安全后,编译器转换为Object类型代替泛型类型




7.泛型的限制


(1)不能使用new E();

   eg:E object = new E();

(2)不能使用new E[ ]

   eg:E[ ] elements = new E[ ];

(3)在静态上下文中不允许类的参数是泛型类型

由于泛型类的所有实例都有相同的运行时类,所以泛型类的静态变量和方法是被它的所有实例共享,因此,在静态方法、数据域或者初始化语句中,为类引用泛型类型参数是非法的,非法代码举例如下:

public class Test<E>{public static void m(E o1){//Illegal}public static E o1;//Illegalstatic {E o2;//Illegal}}


(4)异常类不能是泛型的

必须添加try/catch

泛型类不能扩展java.lang.Throwable

下面类的声明是非法的:

public class MyException<T> extends Exception{}
如果允许,要为MyException<T> 添加一个catch语句

try{}catch( MyException<T> ex){}

JVM必须检查这个try句子抛出的异常,来确定是否与catch子句中指定的类型匹配,但是这是不可能的,因为运行时类型信息是不可得的。



总结


1.泛型具有参数化类型的能力,可以定义使用泛型类型的类或方法,编译器会用具体的类型来替换泛型类型。

2.泛型主要优势是能够在编译时检测错误,而不是运行时。

3.泛型类或方法允许指定这个类或方法可以带有的对象类型,如果试图使用带有不兼容对象的类或方法,编译器会检测出这个错误。

4.定义在类、接口或者静态方法中的泛型称为形式泛型类型,随后可以用一个实际具体类型来替换它。替换泛型类型的过程称为泛型实例化。

5.使用原始类型是为了向后兼容java较早的版本,因为JDK1.5之前是没有泛型的。

6.使用类型消除的方法来实现泛型。编译器使用泛型类型信息来编译代码,但是随后消除它。因此,泛型信息在运行时是不可用的。这个方法能够使泛型代码向后兼容使用原始类型的遗留代码。

7.不能使用泛型类型参数创建实例。

8.不能使用泛型类型参数创建数组。

9.不能在静态环境中使用类的泛型参数。

10.在异常类中不能使用泛型类型参数。




如有错误,欢迎指正。




0 0
原创粉丝点击