一.开篇
上文http://zy19982004.iteye.com/blog/1976993中提到“NewCollections.map() return Map<Object, Object>, but not Map<Integer, String>”,为什么呢?对擦除的理解将是对泛型理解的关键。
二.擦除的概念
《Thinking in Java》里说道“在泛型代码内部,无法获得任何有关泛型参数类型的信息”。
《Java核心技术》里说道“虚拟机没有泛型类型对象-所有对象都属于普通类”。
- Java泛型是使用擦除(擦除实际类型参数,替换为限定类型)来实现的。这意味着当你使用泛型时,泛型实际类型参数只有在静态类型检查期间才出现,在此之后,任何具体的类型信息都被擦除,你唯一知道的就是你在使用一个对象。因此List<String>和List<Integer>在运行时是相同的类型,这两种类型都被擦除为它们的原生类型List。
- 一个例子,看看运行时,类型参数是什么样子。你能够发现的则是用作参数占位符的标识符,这些对我们没什么用。
package com.jyz.study.jdk.generic; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 在泛型代码内部,无法获得任何有关泛型参数类型的信息 * @author JoyoungZhang@gmail.com * */ public class ClassTypeParameters { static class Frob<T>{} static class FrobF<T extends Number>{} static class FrobPM<P,M>{} private static List list1 = new ArrayList(); private static List<Integer> list2 = new ArrayList<Integer>(); private static Map<Integer, Integer> map1 = new HashMap<Integer, Integer>(); static Frob f1 = new Frob(); static FrobF<Integer> f2 = new FrobF<Integer>(); static FrobPM<Integer, Double> f3 = new FrobPM<Integer, Double>(); //Calss.getTypeParameters()将返回一个TypeVariable对象数组 //表示有泛型声明所声明的形式类型参数 public static void main(String[] args) { System.out.println(Arrays.toString(list1.getClass().getTypeParameters())); System.out.println(Arrays.toString(list2.getClass().getTypeParameters())); System.out.println(Arrays.toString(map1.getClass().getTypeParameters())); System.out.println(list1.getClass().getSimpleName()); System.out.println(list2.getClass().getSimpleName()); System.out.println(Arrays.toString(f1.getClass().getTypeParameters())); System.out.println(Arrays.toString(f2.getClass().getTypeParameters())); System.out.println(Arrays.toString(f3.getClass().getTypeParameters())); } } 输出结果 [E] [E] [K, V] ArrayList ArrayList [T] [T] [P, M]
三.为什么要使用擦除
核心动机:使得泛型化的客户端代码可以使用非泛型化的类库,非泛型化的客户端代码可以使用泛型化的类库。这个被称为“兼容迁移性”。这也从侧面反应了,前期的设计多么重要,倘若JDK1.0就将泛型纳入其中,必将是Java使用者的一大福音。
三.擦除原则
- 无限定的形式类型参数将被替换为Object。比喻List<T> T是无限定的,被替换为Object。
- 有限定的形式类型参数将被替换为第一个限定类型。比喻List<T extends Comparable & Serializable>,T被替换为Comparable,也称T被擦除到了Comparable。
- 需要注意的是,泛型擦除的对象是实际参数,也就是说是GenericClass<String, Integer>还是GenericClass<StringBuffer, Number>等具体类型参数被擦除到了ErasureClass,并不是GenericClass<K, V extends Number>被擦除到了ErasureClass。但有时候我们也说GenericClass<K, V extends Number>被擦除到了ErasureClass,甚至可以说GenericClass<?,?>被擦除到了ErasureClass,这并不妨碍我们理解,就好像说所有水果都好吃,自然苹果也是好吃的。
package com.jyz.study.jdk.generic; /** * 擦除存在于泛型类,也存在于泛型方法 * 两者擦除原则一样 * 1.声明形式参数的部分“消失” * 2.无限定的形式类型参数将被替换为Object,有限定的形式类型参数将被替换为第一个实际类型参数 * @author JoyoungZhang@gmail.com * */ public class AfterErasure { } class GenericClass<K, V extends Number>{ private K object1; private V object2; private K get1(){ return object1; } private V get2(){ return object2; } private <KK> KK singleMethod1(KK object){ return object; } private <VV extends Number> VV singleMethod2(VV object){ return object; } } class ErasureClass{ private Object object1; private Number object2; private Object get1(){ return object1; } private Number get2(){ return object2; } private Object singleMethod1(Object object){ return object; } private Number singleMethod2(Number object){ return object; } }
四.擦除的问题
- 既然擦除了类型参数的信息,那编译器是怎么确保方法或类中使用的类型的内部一致性呢?下面这个例子1和2产生的字节码是相同的,一方面验证了上面说的擦除原则;另外一方面说明了泛型工作的地方,称之为边界,在边界处,对传递进来的值就行额外的编译器检查,并插入对传递出去值的转型。
package com.jyz.study.jdk.generic; /** * 1 2字节码相同 * 边界(泛型切入点):对传递进来的值就行额外的编译期检查,并插入对传递出去的值的转型 * @author JoyoungZhang@gmail.com * */ public class TestGenericCheckpoint { public static void main(String[] args) { //1 GenericHolder<String> gh = new GenericHolder<String>(); gh.set("sa");//边界 编译器check String sa1 = gh.get();//运行期间仍会checkcast //2 SimpleHolder sh = new SimpleHolder(); sh.set("sa");//编译器不check任何东西 String sa2 = (String) sh.get();//运行期间checkcast } } class GenericHolder<T>{ private T object; public void set(T object){ this.object = object; } public T get(){ return object; } } class SimpleHolder{ private Object object; public void set(Object object){ this.object = object; } public Object get(){ return this.object; } }
- 擦除后显式的引用运行时类型的操作都将无法工作,包括转型,instanceof,new。有什么补救措施?可以采用类型标签。 isInstance代替 instanceof;newInstance代替new,注意newInstance需要class对象具有默认构造函数。
//Determines if the specified <code>Object</code> is assignment-compatible // * with the object represented by this <code>Class</code>. //This method is // * the dynamic equivalent of the Java language //<code>instanceof</code> // * operator. public native boolean isInstance(Object obj); //Creates a new instance of the class represented by this <tt>Class</tt> // * object. public T newInstance() {...}
相关推荐
本文将深入探讨Java泛型类型擦除的概念,并介绍在类型擦除后,为了保持泛型的安全性和便利性,Java设计者所采取的一些补偿机制。 1. **类型擦除**: - 在编译期间,所有的泛型类型信息都会被替换为它们的实际类型...
Java泛型是一个强大的特性,它提供了类型安全的集合操作和代码重用性。通过理解泛型的工作原理和高级特性,开发者可以编写出更安全、更灵活的代码。尽管泛型有一些限制,但它仍然是现代Java编程中不可或缺的一部分。
本文将深入探讨Java泛型擦除的工作原理、它对编程的影响,以及在实际开发中的应对策略。 Java泛型的类型擦除机制是Java泛型实现的核心,它使得泛型只能在编译时提供类型安全。虽然这种机制带来了一些限制,如运行时...
然而,Java 的泛型在运行时是被擦除的,这意味着在运行时刻,所有的泛型类型信息都会丢失,无法直接用来创建对象或进行类型检查。这导致我们无法使用 `new T()` 或 `instanceof` 这样的操作。为了解决这个问题,我们...
Java泛型的实现基于类型擦除。在编译期间,所有的泛型信息都会被擦除,替换为原始的Object类型。这意味着泛型不提供运行时类型信息,仅用于编译时检查。 四、优势 1. 类型安全:避免了不必要的类型转换,减少了...
5. **泛型擦除**:阐述Java泛型的运行时机制,即泛型信息在编译后会被擦除,因此泛型并不直接改变程序的运行时行为,但类型检查在编译阶段已完成。 6. **类型推断**:描述了Java编译器如何根据上下文自动推断类型...
4. 泛型擦除: - Java的泛型在编译后会被擦除,所有类型参数都会被替换为它们的边界或者Object。 - 这意味着在运行时,泛型类型信息不再存在,但是编译时的类型检查仍然有效。 5. 泛型和集合: - 集合框架如...
解析Java泛型的类型擦除 Java 泛型是 Java SE 1.5 的新特性,它们在语法和应用环境上与 C++ 中的模板相似,但是本质上它们之间有着区别,这种区别就在于 Java 泛型的类型擦除。 Java 泛型的类型擦除是 Java 语言...
- 类型擦除:Java编译器会进行类型擦除,将泛型类的实例转换为无参数类型,但会在编译时进行类型检查。 3. 泛型接口: - 定义与实例化与泛型类类似,例如 `interface MyInterface<T> { ... }`,然后 `MyInterface...
4. **类型擦除**:Java泛型的一个重要特点是类型擦除。在编译后,所有的类型参数都会被替换为它们的边界或者Object,这意味着在运行时,泛型对象与非泛型对象并无实质区别,但编译期的类型检查仍然有效。 5. **创建...
通过学习这些知识点,开发者能更好地理解Java泛型的内部原理,以及如何在实际项目中充分利用泛型的特性,提高代码质量和安全性。在这个视频教程中,张孝祥老师将详细讲解这些概念,并通过实例帮助学员深入掌握Java...
Java泛型是Java 5中引入的一项强大特性,它允许在编译时提供类型安全,同时提高代码的重用性和可读性。泛型的本质是参数化类型,即在类、接口和方法中使用类型作为参数。本文将深入探讨Java泛型的工作原理、应用场景...
Java泛型在编译期之后就会把类型给擦除,在生成的Java字节代码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉。这就意味着,泛型只是对于编译期来说的,在执行期间是没有...
"Java 泛型总结(一):基本用法与类型擦除" Java 泛型是 Java 语言中的一种强大功能,它可以使代码更加简洁、安全。下面是对 Java 泛型的基本用法和类型擦除机制的介绍。 泛型的基本用法 ------------- 泛型是...
- **类型擦除**:Java泛型在编译后会被擦除,但在编译时提供了类型检查。 Maurice Naftalin和Philip Wadler的书籍会详细探讨这些概念,并通过实例解释如何在实际项目中应用泛型和集合。书中的文字版内容可能涵盖: ...
Java泛型是Java编程语言中的一个强大特性,它允许我们在定义类、接口和方法时指定类型参数,从而实现代码的重用和类型安全。在Java泛型应用实例中,我们可以看到泛型如何帮助我们提高代码的灵活性和效率,减少运行时...
Java泛型是Java编程语言中的一个关键特性,它在2004年随着JDK 5.0的发布被引入,极大地增强了类型安全性和代码可读性。泛型允许我们在编写代码时指定容器(如集合)可以存储的数据类型,从而在编译阶段就能捕获类型...
下面我们将深入探讨Java泛型方法的概念、语法以及使用示例。 **一、泛型方法概念** 泛型方法是一种具有类型参数的方法,这些类型参数可以在方法声明时指定,并在方法体内部使用。与类的泛型类似,它们提供了编译时...