Java泛型:类型擦除

类型擦除


显然在平时使用中,ArrayList<Integer>()new ArrayList<String>()是完全不同的类型,但是在这里,程序却的的确确会输出true

这就是Java泛型的类型擦除造成的,因为不管是ArrayList<Integer>()还是new ArrayList<String>(),都在编译器被编译器擦除成了ArrayList。 那编译器为什么要做这件事?原因也和大多数的Java让人不爽的点一样——兼容性。由于泛型并不是从Java诞生就存在的一个特性,而是等到SE5才被加 入的,所以为了兼容之前并未使用泛型的类库和代码,不得不让编译器擦除掉代码中有关于泛型类型信息的部分,这样最后生成出来的代码其实是『泛型无关』的, 我们使用别人的代码或者类库时也就不需要关心对方代码是否已经『泛化』,反之亦然。

在编译器层面做的这件事(擦除具体的类型信息),使得Java的泛型先天都存在一个让人非常难受的缺点:

在泛型代码内部,无法获得任何有关泛型参数类型的信息。

关于getTypeParameters()的解释:

Returns an array of TypeVariable objects that represent the type variables declared by the generic declaration represented by this GenericDeclaration object, in declaration order. Returns an array of length 0 if the underlying generic declaration declares no type variables.

我们期待的是得到泛型参数的类型,但是实际上我们只得到了一堆占位符。

我们无法在泛型内部创建一个T类型的数组,原因也和之前一样,T仅仅是个占位符,并没有真实的类型信息,实际上,除了new表达式之外,instanceof操作和转型(会收到警告)在泛型内部都是无法使用的,而造成这个的原因就是之前讲过的编译器对类型信息进行了擦除。

同时,面对泛型内部形如T var;的代码时,记得多念几遍:它只是个Object,它只是个Object……

虽然有类型擦除的存在,使得编译器在泛型内部其实完全无法知道有关T的任何信息,但是编译器可以保证重要的一点:内部一致性,也是我们放进去的是什么类型的对象,取出来还是相同类型的对象,这一点让Java的泛型起码还是有用武之地的。

代码片段四展现就是编译器确保了我们放在t上的类型的确是T(即便它并不知道有关T的任何类型信息)。这种确保其实做了两步工作:

  • set()处的类型检验
  • get()处的类型转换

这两步工作也成为边界动作

代码片段五同样展示的是泛型的内部一致性。

擦除的补偿


如上看到的,但凡是涉及到确切类型信息的操作,在泛型内部都是无法共工作的。那是否有办法绕过这个问题来编程,答案就是显示地传递类型标签。

代码片段六展示了一种用类型标签生成新对象的方法,但是这个办法很脆弱,因为这种办法要求对应的类型必须有默认构造函数,遇到Integer类型的时候就失败了,而且这个错误还不能在编译器捕获。

进阶的方法可以用限制类型的显示工厂和模板方法设计模式来改进这个问题,具体可以参见《Java编程思想 (第4版)》P382。

代码片段七展示了对泛型数组的擦除补偿,本质方法还是通过显示地传递类型标签,通过Array.newInstance(type, size)来生成数组,同时也是最为推荐的在泛型内部生成数组的方法。

参考链接


Java泛型:类型擦除
java泛型(二)、泛型的内部原理:类型擦除以及类型擦除带来的问题

Android不要使用thread_local定义变量

本来看到Android的ndk都开始用gcc4.8和gcc4.9了,而且gcc4.8.1开始支持全部的c++11的特性,但是Android的run time竟然不支持 thread local storage(TLS),更准确地说,是它没实现。  原来是android的run time没有用gnu的glibc,而是用得Google自己实现的Bionic,很多功能没有实现,留了空接口。 根据维基百科上的说法,Boinic没实现的还不止这个。比glibc而言还不支持的功能还有:

  • 不支持异常处理
  • 无标准模板(这个可以用gnustl或者stlport代替)
  • 不支持宽字符(貌似用处不大,现在主流UTF-8了)
  • 据说它比glibc速度快(也不知道快多少)

目前的解决方案有两个,一个是在TLS的地方排除Android平台,另一个是使用pthread_getspecificpthread_setspecific进行曲线救国。

参考链接


OpenMP on Android - TLS workaround
Android NDK undefined reference to ___tls_get_addr

OpenMediaVault系统升级

最近因为OpenMediaVault升级到2.X,并且声明不再对1.X的版本进行维护,因此需要对OpenMediaVault升级。
1. 首先利用SSH登录命令行,获得root权限。
2. SSH中输入

3.更新完成后输入

4.重新启动即已升级到最新的2.X版本,版本代号Stone burner

JNI DETECTED ERROR IN APPLICATION: input is not valid Modified UTF-8: illegal start byte 0xfe

在使用Jni的JNIEnv->NewStringUTF的时候抛出了异常"JNI DETECTED ERROR IN APPLICATION: input is not valid Modified UTF-8: illegal start byte 0xfe "。网上搜索了一下,这个异常是由于Java虚拟机内部的dalvik/vm/CheckJni.c中的checkUtfString函数抛出的,并且JVM的这个接口明确是不支持四个字节的UTF8字符。因此需要在调用函数之前,对接口传入的字符串进行过滤,过滤函数如下:

参考链接 android jni中 utf-8的check