先看一个例子:

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之datetime模块

    datatime模块重新封装了time模块,提供更多接口,提供的类有:date,time,datetime,timedelta,tzinfo. 1.date类 datetime.date(year, ...

  2. Java&Python&Appium 驱动APP及Desired Capabilities配置

    一.摘要 本篇博文,将阐述如何将环境&代码&APP联系起来工作 二.Java代码 我们先看一段真实的java代码,我将他放在了TestNG框架的@BeforeTest里,执行这段即可启 ...

  3. JavaScript中定义类的方式详解

    本文实例讲述了JavaScript中定义类的方式.分享给大家供大家参考,具体如下: Javascript本身并不支持面向对象,它没有访问控制符,它没有定义类的关键字class,它没有支持继承的exte ...

  4. linux基础_vi和vim快捷键

    (1)拷贝当前行 yy,拷贝当前行向下5行 5yy,并粘贴. (2)删除当前行 dd, 删除当前行向下的5行 5dd. (3)在文件中查找某个单词.[在命令行下使用 /+关键字,回车查找,输入n就是查 ...

  5. 简单的理解 StringBuffer/StringBuilder/String 的区别

    StringBuffer/StringBuilder/String 的区别 这个三类之间主要的区别:运行速度,线程安全两个方面. 速度方面(快到慢): StringBuilder > Strin ...

  6. C# ado.net DataSet使用(五)

    一.填充dataset class Program { private static string constr = "server=.;database=northwnd;integrat ...

  7. 在Google Maps 上点击标签后显示说明

    JS如下: (function() {     window.onload = function() {           // Creating an object literal contain ...

  8. No module named 'pip._vendor.progress.helpers' 的解决方法

    莫名其妙的pip出现故障了 ModuleNotFoundError: No module named ‘pip._vendor.progress.helpers’ 在百度搜索了一圈也没看到这个错误的解 ...

  9. 【原创】洛谷 LUOGU P3373 【模板】线段树2

    P3373 [模板]线段树 2 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某区间每一个数加上x 2.将某区间每一个数乘上x 3.求出某区间每一个数的和 输入输出格式 输入格式: 第 ...

  10. 【线性代数】2-7:转置与变换(Transposes and Permutation)

    title: [线性代数]2-7:转置与变换(Transposes and Permutation) toc: true categories: Mathematic Linear Algebra d ...