Java泛型的一些限制
2015-06-07 10:53
369 查看
本文主要参考《Java编程思想(第4版)》的Java泛型章节,仅当一个简单的读书笔记。
和C++泛型对比,Java泛型只是一种编译期间的擦拭机制。这是由于考虑到和以前的兼容而考虑的一种折中方案。在编译好的泛型代码里,编译期间已经把所有的泛型信息给擦拭掉,因此无法获得任何有关泛型参数类型的信息。因此List<String>和List<Integer>实际上是同一类型。
参考以下代码:
从以上代码可以看到,这种擦拭机制的限制包括如下:
1、instanceof无法使用
2、无法实例化泛型类
3、无法实例化泛型数组
以上3种方法无法通过编译的主要原因是,泛型的擦拭机制把具体的类型信息都擦拭,无法在运行时知道确切的类型信息。不过Java提供了另外的方法去绕过这些限制。
解决1
对于第1种,无法使用instanceof语法,我们可以利用动态的isInstance():
对于第2种,无法实例化泛型类,如果对于要实例化的泛型类,拥有无参默认构造函数,我们可以利用Class对象:
对于第3种,无法实例化泛型数组,我们可以借助ArrayList<T>来代替,但如果一定要获得数组的行为,以及泛型提供的编译期的安全类型,
和C++泛型对比,Java泛型只是一种编译期间的擦拭机制。这是由于考虑到和以前的兼容而考虑的一种折中方案。在编译好的泛型代码里,编译期间已经把所有的泛型信息给擦拭掉,因此无法获得任何有关泛型参数类型的信息。因此List<String>和List<Integer>实际上是同一类型。
参考以下代码:
//以下3个例子都无法通过编译 public <T> void testGeneric(Object arg) { if (arg instanceof T) {} //1 T var = new T(); //2 T[] array = new T[100]; //3 }
从以上代码可以看到,这种擦拭机制的限制包括如下:
1、instanceof无法使用
2、无法实例化泛型类
3、无法实例化泛型数组
以上3种方法无法通过编译的主要原因是,泛型的擦拭机制把具体的类型信息都擦拭,无法在运行时知道确切的类型信息。不过Java提供了另外的方法去绕过这些限制。
解决1
对于第1种,无法使用instanceof语法,我们可以利用动态的isInstance():
//testInstance(String.class, "abc") == true public static <T> boolean testInstance(Class<T> c, Object arg) { return c.isInstance(arg); }<解决2
对于第2种,无法实例化泛型类,如果对于要实例化的泛型类,拥有无参默认构造函数,我们可以利用Class对象:
public static <T> T createGeneric(Class<T> c) { T x = null; try { x = c.newInstance(); } catch (Exception e) { throw new RuntimeException(e); //createGeneric<Integer.class>会抛出异常,由于Integer没有默认构造函数 } return x; }由于以上代码采用Class对象实例化拥有一定限制,因此我们还可以借助工厂方法,显示创建对应的类:
interface GenericFactory<T> { T create(); } class IntegerFactory implements GenericFactory<Integer> { @Override public Integer create() { return new Integer(0); } }解决3
对于第3种,无法实例化泛型数组,我们可以借助ArrayList<T>来代替,但如果一定要获得数组的行为,以及泛型提供的编译期的安全类型,
//GenericArray ga = new GenericArray<Integer>(10); //ga.put(3, 10); //int abc = (Integer) ga.get(3); //Integer[] aaa = (Integer[]) ga.array(); //抛出ClassCastException class GenericArrayEx<T> { private T[] array; public GenericArray(int size) { array = (T[]) new Object[size]; } public void put(int index, T item) { array[index] = item; } public T get(int index) { return array[index]; } public T[] array() { return array; } }对于以上代码,我们采用一个GenericArray的类进行包装,但内部实际还是一个Object[]的对象数组,在put和get的时候获得了编译期间的检查,但问题来了,如果我们使用array()方法企图获得内部数组并转型的时候,会抛出ClassCastException,这是由于数组实际还是Object[]对象!为了解决这个问题,我们可以使用Array.newInstance。
//GenericArrayEx ga = new GenericArrayEx<Integer>(Integer.class, 10); //ga.put(3, 10); //int abc = (Integer) ga.get(3); //Integer[] aaa = (Integer[]) ga.array(); //可以成功转型 class GenericArrayEx<T> { private T[] array; public GenericArrayEx(Class<T> type, int size) { array = (T[]) Array.newInstance(type, size); } public void put(int index, T item) { array[index] = item; } public T get(int index) { return array[index]; } public T[] array() { return array; } }这样,就可以使用T[]了。
相关文章推荐
- Java equals的一个坑
- javaweb生成验证码图片
- java中各种集合的用法和比较
- NetBeans java 编译时提示使用了未经检查或不安全的操作
- java 内部类整理学习
- eclipse在使用git
- maven 在eclipse 中配置下载源代码
- 使用JDBC进行数据访问【spring framwork】
- Eclipse在SVN安装步骤(两种)和使用方法
- 想要成为java高手的25个学习目标
- Acegi Security -- Spring下最优秀的安全系统(zhuan)
- Spring包下载地址
- SpringMVC访问静态资源的三种方式
- javabean总结
- JAVA序列化机制的深入研究
- java获得平台相关的行分隔符和java路径分隔符的方法
- Java for LeetCode 198 House Robber
- java 抽象类与接口的异同
- Java多线程<1>
- JAVA开发读取文件的方法