Java泛型(5):擦除与补偿
先看一个例子:
Class<?> c1 = new ArrayList<String>().getClass();
Class<?> c2 = new ArrayList<Integer>().getClass();
System.out.println(c1 == c2); // true
虽然泛型类的参数不同,但是结果却是TRUE。这是因为在泛型代码内部,无法获得任何有关泛型参数类型的信息。
Java泛型是通过擦除来实现的。这意味着当你在使用泛型时,任何具体的类型信息都被擦除了,你唯一知道的是你在使用一个对象。因此List<String>和List<Integer>在运行时事实上都被擦除成原生的List类型。
运行时,使用泛型和使用Object所产生的字节码是相同的。因此,泛型是在编译期对代码进行检查的。
擦除丢失了在泛型代码中执行某些操作的能力。尽管你一厢情愿想地想把泛型参数替换成为你想要的类型,但是实际上它什么都不是。任何在运行时需要知道确切类型信息的操作都将无法工作。
T t = new T(); // Compile error
T[] t = new T[](); // Compile error
if(c1 instanceof T) {} // Compile error
可以通过引用类型参数来对擦除进行补偿。即显式地传递该类型的class对象。下面介绍了3种方法:
(1) 直接传递一个Class<T>的内建工厂对象。缺点是如果T实例化(newInstance();)失败,编译期不能捕获异常。
class ClassAsFactory<T> {
T x;
public ClassAsFactory(Class<T> kind) {
try {
x = kind.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
class Employee { }
public class InstantiateGenericType {
public static void main(String[] args) {
new ClassAsFactory<Employee>(Employee.class);
System.out.println("ClassAsFactory<Employee> succeeded");
try {
// Integer并没有默认构造器,所以调用newInstance()时会报Error
new ClassAsFactory<Integer>(Integer.class);
} catch (Exception e) {
System.out.println("ClassAsFactory<Integer> failed");
}
}
}
(2) 创建一个显式的工厂对象。它可以很好的限制对象类型。
interface FactoryI<T> {
T create();
}
class Foo<T> {
public final T x;
public <F extends FactoryI<T>> Foo(F factory) {
x = factory.create();
}
}
// 直接实现FactoryI接口
class IntegerFactory implements FactoryI<Integer> {
@Override
public Integer create() {
return new Integer(0);
}
}
// 创建静态内部类实现FactoryI接口
class Widget {
public static class Factory implements FactoryI<Widget> {
@Override
public Widget create() {
return new Widget();
}
}
}
public class FactoryConstraint {
public static void main(String[] args) {
new Foo<Integer>(new IntegerFactory());
new Foo<Widget>(new Widget.Factory());
}
}
(3) 使用模版方法设计模式创建抽象类,由具体的子类实现创建对象。
abstract class GenericWithCreate<T> {
final T element;
GenericWithCreate() {
element = create();
}
abstract T create();
}
class Clazz { }
class Creator extends GenericWithCreate<Clazz> {
Creator() {
System.out.println(element.getClass().getSimpleName());
}
Clazz create() {
Clazz clazz = new Clazz();
return clazz;
}
}
public class CreatorGeneric {
public static void main(String[] args) {
new Creator(); // Clazz
}
}
对于创建泛型数组的情况,一般来说使用ArrayList代替。成功创建泛型数组的唯一方法就是创建一个被擦除类型的新数组(T[] array = (T[]) new Object[size];),并且这个数组的类型只能是Object[]。下面介绍了2种方法:
(1) 使用new Object[size]。缺点是由于擦除,我们无法判定创建好的数组的具体类型。例子中如果改成Integer,则会抛出ClassCastException。
public class GenericArray<T> {
private T[] array;
@SuppressWarnings("unchecked")
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 (T) array[index];
}
public T[] rep() {
return (T[]) array;
}
public static void main(String[] args) {
GenericArray<Integer> gai = new GenericArray<Integer>(10);
for (int i = 0; i < 10; i++)
gai.put(i, i);
// Integer[] ia = gai.rep(); // [ERROR] java.lang.ClassCastException
Object[] ia = gai.rep();
for (int i = 0; i < ia.length; i++)
System.out.print(ia[i] + " "); // 0 1 2 3 4 5 6 7 8 9
}
}
(2) 使用Array.newInstance(type, size)。这时需要传入一个类型标记,就可以解决(1)中的问题。
import java.lang.reflect.Array;
public class GenericArrayWithTypeToken<T> {
private T[] array;
@SuppressWarnings("unchecked")
public GenericArrayWithTypeToken(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[] rep() {
return array;
}
public static void main(String[] args) {
GenericArrayWithTypeToken<Integer> gai = new GenericArrayWithTypeToken<Integer>(Integer.class, 10);
for (int i = 0; i < 10; i++)
gai.put(i, i);
Integer[] ia = gai.rep(); // It's OK
for (int i = 0; i < ia.length; i++)
System.out.print(ia[i] + " "); // 0 1 2 3 4 5 6 7 8 9
}
}
Java泛型(5):擦除与补偿的更多相关文章
- 聊一聊Java泛型的擦除
最近看了<thinking in java>的第十五章泛型,感觉有些东西需要记录下来. 泛型是Java SE5才被引入的概念,现在我的工作中泛型主要使用在集合,这样可以知道set()和ge ...
- java泛型-类型擦除
详细内容:参考java编程思想P373,p650. Java 泛型(Generic)的引入加强了参数类型的安全性,减少了类型的转换,但有一点需要注意:Java 的泛型在编译器有效,在运行期被删除,也就 ...
- 关于Java泛型"擦除"的一点思考
头次写博客,想说的东西不难,关于泛型的疑问,是前一阵在学习jackson中遇到的. 下面就把我所想到的.遇到的,分享出来. 泛型是JDK1.5后的一个特性,是一个参数类型的应用,可以将这个参数声明在类 ...
- Java泛型应用总结
一.泛型的引入原因 在操作集合的时候,之前方法的定义都是Object类型,向集合中添加对象,都自动向上转型,加入的元素可以是任何类型 但是,在取出元素的时候,通常想要使用对象的特有功能,就必须向下转型 ...
- Java泛型全解析【接口、类、封装类型】
目录 1.导读 2.为何需要泛型? 3.泛型的定义格式 3.泛型的好处 4.什么时候使用泛型? 5.泛型的擦除 6.泛型的补偿 7.泛型的应用 7.1[泛型类] ...
- 【Java心得总结三】Java泛型上——初识泛型
一.函数参数与泛型比较 泛型(generics),从字面的意思理解就是泛化的类型,即参数化类型.泛型的作用是什么,这里与函数参数做一个比较: 无参数的函数: public int[] newIntAr ...
- Java编程思想:擦除的补偿(数组泛型处,我有很多细节没有研究)
import sun.net.www.content.text.Generic; import java.lang.reflect.Array; import java.util.ArrayList; ...
- 【Java心得总结四】Java泛型下——万恶的擦除
一.万恶的擦除 我在自己总结的[Java心得总结三]Java泛型上——初识泛型这篇博文中提到了Java中对泛型擦除的问题,考虑下面代码: import java.util.*; public clas ...
- Java泛型:类型擦除
类型擦除 代码片段一 Class c1 = new ArrayList<Integer>().getClass(); Class c2 = new ArrayList<String& ...
随机推荐
- Angular Multiple HTTP Requests with RxJS
原文:https://coryrylan.com/blog/angular-multiple-http-requests-with-rxjs ----------------------------- ...
- CentOS 6 多实例 编译安装mariadb-5.5.59
系统平台: CentOS release 6.9 (Final) 内核 2.6.32-696.el6.x86_64 1.去官网下载适合的源码包 http://mariadb.org/ mariadb- ...
- luogu P4194 矩阵
嘟嘟嘟 先二分. 令二分的值为\(mid\),则对于每一行都要满足\(|\sum_{i = 1} ^ {n} (A_{ij} - B_{ij})|\),把绝对值去掉,就得到了\((\sum_{i = ...
- [bat]删除文件
删除文件 del /f /s /q D:\HRG\NEW_Vn\CSV\*.meta 删除空文件夹 只能先删完文件夹中的文件,再删除空文件夹 rd /s /q D:\HRG\NEW_Vn\CSV\ 脚 ...
- 预处理、const、static与sizeof-#pragma pack的作用
1:有如下代码: #include <iostream.h> #pragma pack(1) struct test{ char c; short s1; short s2; int i; ...
- 用itext生成PDF报错:Font 'STSong-Light1' with 'UniGB-UCS2-H' is not recognized.
用itext生成PDF报错,加上try catch捕获到异常是 BaseFont bFont = BaseFont.createFont("STSong-Light1", &quo ...
- Leetcode题目155.最小栈(简单)
题目描述: 设计一个支持 push,pop,top 操作,并能在常数时间内检索到最小元素的栈. push(x) -- 将元素 x 推入栈中.pop() -- 删除栈顶的元素.top() -- 获取栈顶 ...
- JS基础_数组的方法
常用的方法 1.push:向数组的末尾添加一个或更多元素,并返回新的长度. 将要添加的元素作为方法的参数传递,这些元素将会自动添加到数组的末尾 var a=[1,2,3]; var r = a.pus ...
- fastcgi代理
一.fastcgi代理 1.示意图 2.fastcgi 代理配置语法 a.设置PHP服务代理地址 Syntax: fastcgi_pass address; Default: — Context: l ...
- ajax修改表单的值后dom没更新的解决办法
添加一个扩展方法,通过$("#id").html($("#id").formhtml())更改. 扩展方法: (function ($) { var oldHT ...