先看一个例子:

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. 点击a链接防止滚动条滚动

    href="javascript:void(0)"而不是 href="#"

  2. Spring入门篇——第1章 概述

    第1章 概述 本章对课程的情况进行介绍,并介绍框架和Spring概况. 1-1 Spring入门课程简介 1-2 Spring概况 1-3 Spring框架

  3. 【转载】总结:几种生成HTML格式测试报告的方法

    总结:几种生成HTML格式测试报告的方法 写自动化测试时,一个很重要的任务就是生成漂亮的测试报告. 1.用junit或testNg时,可以用ant辅助生成html格式: <target name ...

  4. HDU 6070 - Dirt Ratio | 2017 Multi-University Training Contest 4

    比赛时会错题意+不知道怎么线段树维护分数- - 思路来自题解 /* HDU 6070 - Dirt Ratio [ 二分,线段树 ] | 2017 Multi-University Training ...

  5. SQL SERVER 表添加新字段

    SQL SERVER 表添加新字段 ALTER TABLE doc_exa ADD column_b VARCHAR(20) NULL; -- doc_exa 是表名 -- column_b 是新加的 ...

  6. PHP mysqli_fetch_all() 函数

    从结果集中取得所有行作为关联数组: <?php // 假定数据库用户名:root,密码:123456,数据库:RUNOOB $con=mysqli_connect("localhost ...

  7. 如何复制word的图文到ueditor中自动上传?

    官网地址http://ueditor.baidu.com Git 地址 https://github.com/fex-team/ueditor 参考博客地址 http://blog.ncmem.com ...

  8. 【8.27-模拟赛】remove

    题解: 代码: #include<iostream> #include<algorithm> #include<cstdio> #include<cstrin ...

  9. Financial Management(SDUT 1007)

    Problem Description Larry graduated this year and finally has a job. He's making a lot of money, but ...

  10. Django基础之JsonResponse对象

    JsonResponse是HttpResponse的子类, 专门用来生成JSON编码的响应. from django.http import JsonResponse response = JsonR ...