Java泛型
时间:2015-05-15 13:48:21
收藏:0
阅读:154
一、了解泛型
泛型是jdk1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。 Java语言引入泛型的好处是安全简单。
在jdk1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患,如以下代码所示:
//此时集合以Object形式接收任意类型 ArrayList array=new ArrayList(); array.add(1);//整型 array.add(1L);//浮点型 array.add("abc");//字符型 //编译没问题,运行期报错 int i=(Integer)array.get(1);
在jdk1.5之后,类似的类型安全问题被解决,解决方式便是--泛型,此时我们再来看引入泛型机制的代码:
//以下集合在创建时加入了泛型参数,限制了其中数据类型 ArrayList<Integer> array=new ArrayList<Integer>(); array.add(1); //array.add(1L);编译报错 //array.add("abc");编译报错 //没问题 int i=(Integer)array.get(0);可以看出,我们在集合创建时便以泛型参数的方式为集合中的数据限制了具体类型,所以在我们取数据时便可知道真实的数据类型,不会再出现类型转换错误,保证了类型安全。
在以上代码中,我们可以看到,当我们限制了参数类型为Integer型之后,我们再向集合中添加其他类型时,编译器便会报错,那么泛型是不是作用编译器呢?
确实,泛型是提供给javac编译器使用的,可以限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器编译带类型参数的集合时会去掉“参数类型信息”,使程序运行效率不收影响,对于参数化的泛型类型,getClass()方法返回的返回值和原始类型完全一样。由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可以往某个泛型集合中加入其它类型的数据,例如,用反射得到集合,在调用add方法即可,如下代码所示:
public class GenericTest { public static void main(String[] args) throws SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { /** * 泛型是提供给javac编译器使用的,只要能跳过编译器,则可以向注明数据类型的集合中添加其他类型的数据。 * 该操作可以通过反射实现,反射在运行期。 */ //指明集合数据类型为Integer,无法在编译期间添加Integer以外的类型 List<Integer> all=new ArrayList<Integer>(); //通过反射跳过编译器,向集合中添加String类型数据 all.getClass().getMethod("add", Object.class).invoke(all,"abc"); //此时输出:abc System.out.println(all.get(0));//abc } }
二、泛型术语、限制
2.1 术语
ArrayList<E>类定义和ArrayList<Integer>类引用中涉及的术语:
1、ArrayList<E>整体称为泛型类型
2、ArrayList<E>中的E称为类型变量或类型参数
3、整个ArrayList<Integer>称为参数化的类型
4、ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数
5、ArrayList<Integer>念着typeof
6、ArrayList称为原始类型
2.2 限制
1、参数化类型可以引用一个原始类型的对象,编译报警告,如下代码所示:
Collection<String> c=new ArrayList();2、原始类型可以引用一个参数化类型的对象,编译报警告,如下代码所示:
Collection c=new ArrayList<String>();3、参数化类型不考虑类型参数的继承关系
<pre name="code" class="html">Collection<String> c=new ArrayList<String>();//正确 Collection<Object> c=new ArrayList<String>();//错误 Collection<String> c=new ArrayList<Object>();//错误 //但是以下代码却是正确的 Collection c=new ArrayList<String>(); Collection<Object> c1=c; //这是因为c此时是一个原始类型,而参数化的类型可以接收原始类型,千万不要进行替换操作
4、在创建数组实例时,数组的元素不能使用参数化的类型,例如下面代码便是错误的:
Vector<Integer>[] vector=new Vector<Integer>[];
5、泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。
6、同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。7、泛型的类型参数可以有多个
8、泛型的参数类型可以使用extends语句,例如<T extends superclass>。习惯上称为“有界类型”。
9、泛型的参数类型还可以是通配符类型。例如Class<?> classType = Class.forName("java.lang.String");
9、泛型的参数类型还可以是通配符类型。例如Class<?> classType = Class.forName("java.lang.String");
三、泛型通配符?
3.1、简介
泛型通配符?可以引用其他各种参数化的类型,?通配符定的变量主要作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法,事例代码如下:
/** * 泛型通配符?使用,接收具有任意参数类型的集合 * 使用通配符,不能对集合进行具体类型的操作,如add(E e) * 只能使用与类型无关的方法,如size(); * @param collection */ public void printCollection(Collection<?> collection){ for(Object obj:collection){ System.out.println(obj.toString()); } }
3.2 通配符扩展
1、限定通配符的上边界(限定通配符总是包括自己)
</pre><pre name="code" class="html"><pre name="code" class="html"><pre name="code" class="html" style="font-size:18px;">Collection<? extends Number> c =new ArrayList<Integer>();2、限定通配符的下边界(限定通配符总是包括自己)
Collection<? super Integer> c =new ArrayList<Number>();
四、自定义泛型方法
是否拥有泛型方法,与其所在的类是否泛型没有关系。要定义泛型方法,只需将泛型参数列表置于返回值前,事例如下:
需要注意,一个static方法,无法访问泛型类的类型参数,所以,若要static方法需要使用泛型能力,必须使其成为泛型方法。
/** * 自定义泛型方法 * <T>:声明自定义泛型 * T:返回值 */ public static <T> T add(T x,T y){ return null; }
<span style="font-size:18px;"> //调用自定义泛型方法 add(3, 5); add(3.5, 5); Number result=add(3.5, 6);</span>使用泛型方法时,不必指明参数类型,编译器会自己找出具体的类型。泛型方法除了定义不同,调用就像普通方法一样。
需要注意,一个static方法,无法访问泛型类的类型参数,所以,若要static方法需要使用泛型能力,必须使其成为泛型方法。
五、泛型应用于异常
泛型也可以爱异常上使用,事例代码如下:
/** * 泛型用于与异常 * 1、T必须是Exception的子类 * 2、catch参数中不能使用该方式:T e */ public static <T extends Exception> void sayHello() throws T{ try { // } catch (Exception e) {//catch参数中不能使用该方式:T e throw (T)e;//可以将异常包装为T抛出 } }
评论(0)