先看一个例子:

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. iframe通信相关:父操作子,子操作父,兄弟通信

    这里写window和document是认为代表了BOM和DOM(个人理解不一定对) 拿到了window就能操作全局变量和函数 拿到了document就能获取dom,操作节点 父操作子 window:选 ...

  2. VSCODE常用插件使用记录

    常用必备: 1.vscode-icon 让 vscode 资源树目录加上图标,必备良品! 2.Path Intellisense 自动路劲补全,默认不带这个功能的 3.beautify Beautif ...

  3. STM32调试利器之ITM

    原创: Osprey  鱼鹰谈单片机 2月17日 STM32 有一个代码跟踪功能,即 ITM,这个调试功能非常强大,可以替代串口输入输出功能,而且只需要占用一根 I/O 线就可以实现.当然它的好处不仅 ...

  4. BZOJ 3744 Gty的妹子序列 做法集结

    我只会O(nnlogn)O(n\sqrt nlogn)O(nn​logn)的 . . . . 这是分块+树状数组+主席树的做法O(nnlogn)O(n\sqrt nlogn)O(nn​logn) 搬来 ...

  5. 题解 [CF803C] Maximal GCD

    题面 解析 一开始以为这题很难的... 其实只要设\(d\)为\(a\)的最大公因数, 即\(a[i]=s[i]*d\), 因为\(n=\sum_{i=1}^{n}a[i]=\sum_{i=1}^ns ...

  6. 题解 [BZOJ4144] Petrol

    题目描述 ​ 有一张 n 个点 m 条边的无向图,其中有 s 个点上有加油站.有 Q 次询问(a,b,c), 问能否开一辆油箱容积为 c 的车从 a 走到 b.(a,b均为加油站) 输入格式 ​ 第一 ...

  7. springboot注释层分解图

  8. HGOI 20191107 题解

    Problem A 树状数组 给出下列$C++$代码: 设区间加操作$modify(l,r)$为调用两次$update(r,1)$和$update(l-1,-1)$ 设$f(l,r)$表示在初始$cn ...

  9. Codeforces 915 F. Imbalance Value of a Tree(并查集)

    F. Imbalance Value of a Tree 题意: 给一颗带点权的树,求所有简单路径上最大点权和最小点权之差的总和. 思路: 所求问题可以看作求各路径上的最大值之和减各路径上的最小值之和 ...

  10. From 7.29 To 8.4

    From 7.29 To 8.4 大纲 英语按时背 做点思维题 可能还有时间学点东西, 这周我也不知道应该干什么 7.29 上午考试, 终于有一回不是自闭的考试了 题目比较简单, 就不说了 7.30 ...