先看一个例子:

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):擦除与补偿的更多相关文章

  1. 聊一聊Java泛型的擦除

    最近看了<thinking in java>的第十五章泛型,感觉有些东西需要记录下来. 泛型是Java SE5才被引入的概念,现在我的工作中泛型主要使用在集合,这样可以知道set()和ge ...

  2. java泛型-类型擦除

    详细内容:参考java编程思想P373,p650. Java 泛型(Generic)的引入加强了参数类型的安全性,减少了类型的转换,但有一点需要注意:Java 的泛型在编译器有效,在运行期被删除,也就 ...

  3. 关于Java泛型"擦除"的一点思考

    头次写博客,想说的东西不难,关于泛型的疑问,是前一阵在学习jackson中遇到的. 下面就把我所想到的.遇到的,分享出来. 泛型是JDK1.5后的一个特性,是一个参数类型的应用,可以将这个参数声明在类 ...

  4. Java泛型应用总结

    一.泛型的引入原因 在操作集合的时候,之前方法的定义都是Object类型,向集合中添加对象,都自动向上转型,加入的元素可以是任何类型 但是,在取出元素的时候,通常想要使用对象的特有功能,就必须向下转型 ...

  5. Java泛型全解析【接口、类、封装类型】

    目录   1.导读  2.为何需要泛型?   3.泛型的定义格式   3.泛型的好处  4.什么时候使用泛型?   5.泛型的擦除   6.泛型的补偿  7.泛型的应用      7.1[泛型类]   ...

  6. 【Java心得总结三】Java泛型上——初识泛型

    一.函数参数与泛型比较 泛型(generics),从字面的意思理解就是泛化的类型,即参数化类型.泛型的作用是什么,这里与函数参数做一个比较: 无参数的函数: public int[] newIntAr ...

  7. Java编程思想:擦除的补偿(数组泛型处,我有很多细节没有研究)

    import sun.net.www.content.text.Generic; import java.lang.reflect.Array; import java.util.ArrayList; ...

  8. 【Java心得总结四】Java泛型下——万恶的擦除

    一.万恶的擦除 我在自己总结的[Java心得总结三]Java泛型上——初识泛型这篇博文中提到了Java中对泛型擦除的问题,考虑下面代码: import java.util.*; public clas ...

  9. Java泛型:类型擦除

    类型擦除 代码片段一 Class c1 = new ArrayList<Integer>().getClass(); Class c2 = new ArrayList<String& ...

随机推荐

  1. Python的安装和配置(windowns 双版本)

    1.去官网上下载python,注意版本. 官网地址:https://www.python.org/downloads/windows 2.下载安装版或者zip包都可以.安装就按向导一步一步完成即可.z ...

  2. Spring入门篇——第4章 Spring Bean装配(下)

    第4章 Spring Bean装配(下) 介绍Bean的注解实现,Autowired注解说明,基于java的容器注解说明,以及Spring对JSR支持的说明 4-1 Spring Bean装配之Bea ...

  3. 模块化开发之Amd规范和Cmd规范

    CMD规范:是SeaJS 在推广过程中对模块定义的规范化产出的. AMD规范:是 RequireJS 在推广过程中对模块定义的规范化产出的 // CMD define(function(require ...

  4. Qt中PushButton的pressed,released,clicked三种响应的区别

    Qt的PushButton的常用的三种响应有pressed,released和clicked. 优先级:pressed>released>clicked 按下按钮pressed函数的内容, ...

  5. 使用yum命令出错:SyntaxError: invalid syntax 由于用户取消而退出

    详见: https://blog.csdn.net/qq_24880013/article/details/90731617 必须修改的两个yum配置文件: 因为yum使用python2,因此替换为p ...

  6. 牛客寒假算法基础集训营2 【处女座与复读机】DP最小编辑距离【模板题】

    链接:https://ac.nowcoder.com/acm/contest/327/G来源:牛客网 一天,处女座在牛客算法群里发了一句“我好强啊”,引起无数的复读,可是处女座发现复读之后变成了“处女 ...

  7. pat 甲级 1034 ( Head of a Gang )

    1034 Head of a Gang (30 分) One way that the police finds the head of a gang is to check people's pho ...

  8. bbs--文章后台

    bbs--文章后台 需求分析 在个人文章后台中 1 有添加文章的接口 展示文章标题输入框 文本编辑框 标签 分类 选择框 提交 2 编辑文章的接口 页面与添加文章相同,就是把该文章的内容展示到文本编辑 ...

  9. TypeScript 技巧

    前言 很早以前就尝试过使用 TypeScript 来进行日常编码,但自己对静态类型语言的了解并不深入,再加上 TypeScript 的类型系统有着一定的复杂度,因此感觉自己并没有发挥好这门语言的优势, ...

  10. 函数柯里化与偏函数+bind

    简单理解: 1,函数柯里化就是把多参数函数分解为多return的单参数函数: 举个例子(伪代码): function func (a, b, c){ return } 柯里化为 function fu ...