`
zy19982004
  • 浏览: 661841 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
博客专栏
F6f66edc-1c1a-3859-b76b-a22e740b7aa7
Hadoop学习
浏览量:251950
社区版块
存档分类
最新评论

Java泛型学习二:擦除

 
阅读更多

一.开篇

     上文http://zy19982004.iteye.com/blog/1976993中提到“NewCollections.map() return Map<Object, Object>, but not  Map<Integer, String>”,为什么呢?对擦除的理解将是对泛型理解的关键。

 

二.擦除的概念

     《Thinking in Java》里说道“在泛型代码内部,无法获得任何有关泛型参数类型的信息”。

     《Java核心技术》里说道“虚拟机没有泛型类型对象-所有对象都属于普通类”。

  1. Java泛型是使用擦除(擦除实际类型参数,替换为限定类型)来实现的。这意味着当你使用泛型时,泛型实际类型参数只有在静态类型检查期间才出现,在此之后,任何具体的类型信息都被擦除,你唯一知道的就是你在使用一个对象。因此List<String>和List<Integer>在运行时是相同的类型,这两种类型都被擦除为它们的原生类型List。
  2. 一个例子,看看运行时,类型参数是什么样子。你能够发现的则是用作参数占位符的标识符,这些对我们没什么用。
    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使用者的一大福音。

 

三.擦除原则

  1. 无限定的形式类型参数将被替换为Object。比喻List<T> T是无限定的,被替换为Object。
  2. 有限定的形式类型参数将被替换为第一个限定类型。比喻List<T extends Comparable & Serializable>,T被替换为Comparable,也称T被擦除到了Comparable。
  3. 需要注意的是,泛型擦除的对象是实际参数,也就是说是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. 既然擦除了类型参数的信息,那编译器是怎么确保方法或类中使用的类型的内部一致性呢?下面这个例子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;
        }
    }
    
     
  2. 擦除后显式的引用运行时类型的操作都将无法工作,包括转型,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泛型类型擦除的概念,并介绍在类型擦除后,为了保持泛型的安全性和便利性,Java设计者所采取的一些补偿机制。 1. **类型擦除**: - 在编译期间,所有的泛型类型信息都会被替换为它们的实际类型...

    Java泛型深入解析:类型安全的灵活编程

    Java泛型是一个强大的特性,它提供了类型安全的集合操作和代码重用性。通过理解泛型的工作原理和高级特性,开发者可以编写出更安全、更灵活的代码。尽管泛型有一些限制,但它仍然是现代Java编程中不可或缺的一部分。

    Java泛型擦除深度解析:原理、影响与编程实践

    本文将深入探讨Java泛型擦除的工作原理、它对编程的影响,以及在实际开发中的应对策略。 Java泛型的类型擦除机制是Java泛型实现的核心,它使得泛型只能在编译时提供类型安全。虽然这种机制带来了一些限制,如运行时...

    Java 泛型擦除后的三种补救方法

    然而,Java 的泛型在运行时是被擦除的,这意味着在运行时刻,所有的泛型类型信息都会丢失,无法直接用来创建对象或进行类型检查。这导致我们无法使用 `new T()` 或 `instanceof` 这样的操作。为了解决这个问题,我们...

    java泛型技术之发展

    Java泛型的实现基于类型擦除。在编译期间,所有的泛型信息都会被擦除,替换为原始的Object类型。这意味着泛型不提供运行时类型信息,仅用于编译时检查。 四、优势 1. 类型安全:避免了不必要的类型转换,减少了...

    java泛型学习全面页面下载资料

    5. **泛型擦除**:阐述Java泛型的运行时机制,即泛型信息在编译后会被擦除,因此泛型并不直接改变程序的运行时行为,但类型检查在编译阶段已完成。 6. **类型推断**:描述了Java编译器如何根据上下文自动推断类型...

    关于java基础的泛型的练习

    4. 泛型擦除: - Java的泛型在编译后会被擦除,所有类型参数都会被替换为它们的边界或者Object。 - 这意味着在运行时,泛型类型信息不再存在,但是编译时的类型检查仍然有效。 5. 泛型和集合: - 集合框架如...

    解析Java泛型的类型擦除.pdf

    解析Java泛型的类型擦除 Java 泛型是 Java SE 1.5 的新特性,它们在语法和应用环境上与 C++ 中的模板相似,但是本质上它们之间有着区别,这种区别就在于 Java 泛型的类型擦除。 Java 泛型的类型擦除是 Java 语言...

    Java泛型_Java中的泛型结构_

    - 类型擦除:Java编译器会进行类型擦除,将泛型类的实例转换为无参数类型,但会在编译时进行类型检查。 3. 泛型接口: - 定义与实例化与泛型类类似,例如 `interface MyInterface&lt;T&gt; { ... }`,然后 `MyInterface...

    SUN公司Java泛型编程文档

    4. **类型擦除**:Java泛型的一个重要特点是类型擦除。在编译后,所有的类型参数都会被替换为它们的边界或者Object,这意味着在运行时,泛型对象与非泛型对象并无实质区别,但编译期的类型检查仍然有效。 5. **创建...

    java泛型的内部原理及更深应用

    通过学习这些知识点,开发者能更好地理解Java泛型的内部原理,以及如何在实际项目中充分利用泛型的特性,提高代码质量和安全性。在这个视频教程中,张孝祥老师将详细讲解这些概念,并通过实例帮助学员深入掌握Java...

    Java泛型的深度解析:原理、应用与最佳实践

    Java泛型是Java 5中引入的一项强大特性,它允许在编译时提供类型安全,同时提高代码的重用性和可读性。泛型的本质是参数化类型,即在类、接口和方法中使用类型作为参数。本文将深入探讨Java泛型的工作原理、应用场景...

    很好的Java泛型的总结

    Java泛型在编译期之后就会把类型给擦除,在生成的Java字节代码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉。这就意味着,泛型只是对于编译期来说的,在执行期间是没有...

    Java 泛型总结(一):基本用法与类型擦除

    "Java 泛型总结(一):基本用法与类型擦除" Java 泛型是 Java 语言中的一种强大功能,它可以使代码更加简洁、安全。下面是对 Java 泛型的基本用法和类型擦除机制的介绍。 泛型的基本用法 ------------- 泛型是...

    [Java泛型和集合].(Java.Generics.and.Collections).文字版

    - **类型擦除**:Java泛型在编译后会被擦除,但在编译时提供了类型检查。 Maurice Naftalin和Philip Wadler的书籍会详细探讨这些概念,并通过实例解释如何在实际项目中应用泛型和集合。书中的文字版内容可能涵盖: ...

    Java泛型应用实例

    Java泛型是Java编程语言中的一个强大特性,它允许我们在定义类、接口和方法时指定类型参数,从而实现代码的重用和类型安全。在Java泛型应用实例中,我们可以看到泛型如何帮助我们提高代码的灵活性和效率,减少运行时...

    java泛型例子

    Java泛型是Java编程语言中的一个关键特性,它在2004年随着JDK 5.0的发布被引入,极大地增强了类型安全性和代码可读性。泛型允许我们在编写代码时指定容器(如集合)可以存储的数据类型,从而在编译阶段就能捕获类型...

    java 泛型方法使用示例

    下面我们将深入探讨Java泛型方法的概念、语法以及使用示例。 **一、泛型方法概念** 泛型方法是一种具有类型参数的方法,这些类型参数可以在方法声明时指定,并在方法体内部使用。与类的泛型类似,它们提供了编译时...

Global site tag (gtag.js) - Google Analytics