精华内容
下载资源
问答
  • java 泛型和类型擦除 “编译期间擦除泛型”是常识(好吧,类型参数实参实际上是被擦除的)。 这是由于“类型擦除”而发生的。 但这是错误的,正如许多开发人员所假设的那样,将<..>符号内指定的所有内容都...

    java 泛型和类型擦除

    “编译期间擦除泛型”是常识(好吧,类型参数和实参实际上是被擦除的)。 这是由于“类型擦除”而发生的。 但这是错误的,正如许多开发人员所假设的那样,将<..>符号内指定的所有内容都删除了。 请参见下面的代码:

    public class ClassTest {
      public static void main(String[] args) throws Exception {
        ParameterizedType type = (ParameterizedType) 
           Bar.class.getGenericSuperclass();
        System.out.println(type.getActualTypeArguments()[0]);
        
        ParameterizedType fieldType = (ParameterizedType) 
            Foo.class.getField("children").getGenericType();
        System.out.println(fieldType.getActualTypeArguments()[0]);
        
        ParameterizedType paramType = (ParameterizedType) 
            Foo.class.getMethod("foo", List.class)
            .getGenericParameterTypes()[0];
        System.out.println(paramType.getActualTypeArguments()[0]);
        
        System.out.println(Foo.class.getTypeParameters()[0]
            .getBounds()[0]);
      }
      
      class Foo<E extends CharSequence> {
        public List<Bar> children = new ArrayList<Bar>();
        public List<StringBuilder> foo(List<String> foo) {return null; }
        public void bar(List<? extends String> param) {}
      }
       
      class Bar extends Foo<String> {}
    }

    你知道那是什么吗?

    类java.lang.String
    类ClassTest $ Bar
    类java.lang.String
    类java.lang.StringBuilder
    接口java.lang.CharSequence

    您会看到每个类型的参数都会保留下来,并且可以在运行时通过反射进行访问。 但是,什么是“类型擦除”? 必须删除某些内容吗? 是。 实际上,除结构化结构外,其他所有结构均与之相关–上面的所有内容都与类的结构有关,而不是与程序流有关。 换句话说,有关类的类型参数及其字段和方法的元数据被保留以通过反射进行访问。

    但是,其余部分将被删除。 例如,以下代码:

    List<String> list = new ArrayList<>();
    Iterator<String> it = list.iterator();
    while (it.hasNext()) {
       String s = it.next();
    }

    实际上将被转换为此(两个片段的字节码相同):

    List list = new ArrayList();
    Iterator it = list.iterator();
    while (it.hasNext()) {
       String s = (String) it.next();
    }

    因此,将删除您在方法主体中定义的所有类型参数,并在需要的地方添加强制类型转换。 另外,如果定义了一个方法以接受List<T> ,则此T将被转换为Object(或如果声明了其边界,则转换为它的边界。这就是为什么您不能执行new T() (顺便说一句,有关此擦除的公开问题 )。

    到目前为止,我们已经介绍了类型擦除定义的前两点。 第三个是关于桥接方法。 我已经用这个stackoverflow问题(和答案)进行了说明

    所有这两个“道德”。 首先,java泛型很复杂。 但是您可以在不了解所有复杂性的情况下使用它们。

    其次,不要假设所有类型信息都被删除了-结构类型参数在那里,因此,如果需要,可以使用它们(但不要过分依赖反射)。

    翻译自: https://www.javacodegeeks.com/2014/11/on-java-generics-and-erasure.html

    java 泛型和类型擦除

    展开全文
  •  Java泛型在使用过程有诸多的问题,如不存在List&lt;String&gt;.class, List&lt;Integer&gt;不能赋值给List&lt;Number&gt;(不可协变),奇怪的ClassCastException等。 正确的使用Java泛型...

    http://justjavac.iteye.com/blog/1741638

     

    一、概述

          Java泛型在使用过程有诸多的问题,如不存在List<String>.class, List<Integer>不能赋值给List<Number>(不可协变),奇怪的ClassCastException等。 正确的使用Java泛型需要深入的了解Java的一些概念,如协变,桥接方法,以及这篇笔记记录的类型擦除。Java泛型的处理几乎都在编译器中进行,编译器生成的bytecode是不包涵泛型信息的,泛型类型信息将在编译处理是被擦除,这个过程即类型擦除。

    二、编译器如何处理泛型?

         通常情况下,一个编译器处理泛型有两种方式:
         1.Code specialization。在实例化一个泛型类或泛型方法时都产生一份新的目标代码(字节码or二进制代码)。例如,针对一个泛型list,可能需要 针对string,integer,float产生三份目标代码。
         2.Code sharing。对每个泛型类只生成唯一的一份目标代码;该泛型类的所有实例都映射到这份目标代码上,在需要的时候执行类型检查和类型转换。
         C++中的模板(template)是典型的Code specialization实现。C++编译器会为每一个泛型类实例生成一份执行代码。执行代码中integer list和string list是两种不同的类型。这样会导致代码膨胀(code bloat),不过有经验的C++程序员可以有技巧的避免代码膨胀。
         Code specialization另外一个弊端是在引用类型系统中,浪费空间,因为引用类型集合中元素本质上都是一个指针。没必要为每个类型都产生一份执行代码。而这也是Java编译器中采用Code sharing方式处理泛型的主要原因。
         Java编译器通过Code sharing方式为每个泛型类型创建唯一的字节码表示,并且将该泛型类型的实例都映射到这个唯一的字节码表示上。将多种泛型类形实例映射到唯一的字节码表示是通过类型擦除(type erasue)实现的。

    三、 什么是类型擦除?

         类型擦除指的是通过类型参数合并,将泛型类型实例关联到同一份字节码上。编译器只为泛型类型生成一份字节码,并将其实例关联到这份字节码上。类型擦除的关键在于从泛型类型中清除类型参数的相关信息,并且再必要的时候添加类型检查和类型转换的方法。
         类型擦除可以简单的理解为将泛型java代码转换为普通java代码,只不过编译器更直接点,将泛型java代码直接转换成普通java字节码。
         类型擦除的主要过程如下:
         1.将所有的泛型参数用其最左边界(最顶级的父类型)类型替换。
         2.移除所有的类型参数。

     

    五、Just remember

    1.虚拟机中没有泛型,只有普通类和普通方法
    2.所有泛型类的类型参数在编译时都会被擦除
    3.创建泛型对象时请指明类型,让编译器尽早的做参数检查(Effective Java,第23条:请不要在新代码中使用原生态类型)
    4.不要忽略编译器的警告信息,那意味着潜在的ClassCastException等着你。

    展开全文
  • java泛型和类型擦除

    2020-09-03 14:35:35
    java泛型和类型擦除 泛型的本质是参数化类型,这种参数类型可以用在、接口方法的创建中。泛型是在JAVA 1.5版本中才引入的,它能以前的版本兼容的原因是泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型...

    java泛型和类型擦除
    泛型的本质是参数化类型,这种参数类型可以用在类、接口和方法的创建中。泛型是在JAVA 1.5版本中才引入的,它能和以前的版本兼容的原因是泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,即类型擦除。
    根据使用情况可以分为以下三种:
    泛型类
    泛型方法
    泛型接口
    下面是一个常用的泛型类:

    // 一个泛型类,可以根据需要包装不同结果的返回值
    public class Result<T> {private boolean success;private String message;private T data;// 一个泛型方法
        // 返回值类型定义前的<T>是必须的,用来声明一个类型持有者名称,然后就可以把T当作一个类型代表来声明成员、参数和返回值类型。
        public static <T> Result<T> success(T data) {
            Result<T> r = new Result<>();
            r.success = true;
            r.data = data;
            return r;
        }public static <T> Result<T> error(String message) {
            Result<T> r = new Result<>();
            r.message = message;
            return r;
        }// getter & setter
    }
    

    为什么要用T而不是其它字母?事实上是可以任意字符串(如Result< something >),但是为了显得专业,一般约定几个大写字母在不同场景使用。

    T 最常用,一般代指任意类,不知道用啥就用它
    E 代表Element,一般用在集合的泛型场景
    K 代表Key,一般和Value一起出现在键值对场景(如Entry)
    V 代表Value,一般和Key一起出现在键值对场景(如Entry)
    还有些不太常见的如S,U…
    类型擦除

    在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,以提高代码的重用率。

    import java.lang.reflect.Field;
    import java.util.Date;public class Main {public static void main(String[] args) throws NoSuchFieldException {
            Result<Date> r1 = Result.success(new Date());
            Result<Number> r2 = Result.success(2.333);
            dataType(r1);
            dataType(r2);
        }private static void dataType(Result<?> result) throws NoSuchFieldException {
            Field field = result.getClass().getDeclaredField("data");
            System.out.println(field.getType().toString());
        }}/* 输出:
    ​
    class java.lang.Object
    class java.lang.Object
    ​
    */
    

    通过反射我们在运行时得到了data的类型,发现都是Object,证明代码编译后所谓泛型都没了,这就是泛型擦除。
    通过反射绕过泛型限制

    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.Date;public class Main {public static void main(String[] args) throws Exception {
            ArrayList<Integer> list = new ArrayList<Integer>();
            //正规途径
            list.add(1);
            //反射大法
            Method m = list.getClass().getMethod("add", Object.class);
            m.invoke(list, 2);
            m.invoke(list, 3.21);
            m.invoke(list, "对不起,我是字符串");
            m.invoke(list, new Date());
            for (Object x : list) {
                System.out.println(x.getClass().getName() + ":\t" + x);
            }
        }
    }/* 输出:
    ​
    java.lang.Integer:    1
    java.lang.Integer:    2
    java.lang.Double:    3.21
    java.lang.String:    对不起,我是字符串
    java.util.Date:    Sun Jul 28 23:49:34 CST 2019
    ​
    */
    
    展开全文
  • Java泛型泛型擦除

    千次阅读 2018-07-01 14:09:11
    这种参数类型可被应用于、接口方法中,分别被称为泛型、泛型接口、泛型方法。在泛型出现之前,Java是通过Object是所有类型的父类类型强制转换这两个特性来实现类型泛化的。 泛型擦除 Jav...

    目录

    Java泛型

    泛型是JDK1.5的一项新增特性,它的本质是参数化类型应用,也就是说所操作的数据类型被指定为一个参数。这种参数类型可被应用于类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。在泛型出现之前,Java是通过Object是所有类型的父类和类型强制转换这两个特性来实现类型泛化的。

    泛型擦除

    Java语言中泛型只存在于程序源码中,在编译后的字节码(.class)文件中,就已经替换为原来的原生类型(也叫裸类型)了,并且在相应的地方插入了强制转换类型的代码,因此,对于运行期的Java语言来说,ArrayList<int>ArrayList<String>就是同一个类,所以泛型技术实际上是Java语言的一个语法糖,这种泛型实现方法称为类型擦除,又叫做伪泛型。
    泛型擦除前:

    public static void main(String[] args) {
            Map<String,String> map = new HashMap<String, String>();
            map.put("hello","hello");
            map.put("value","value");
            System.out.println(map.get("hello"));
            System.out.println(map.get("value"));
        }

    把这段代码编译成class文件,然后再反编译后,会发现泛型不见了,泛型类型都变成了原生类型,在使用的地方进行强制转换,如下:

    public static void main(String[] args) {
            Map map = new HashMap();
            map.put("hello","hello");
            map.put("value","value");
            System.out.println((String) map.get("hello"));
            System.out.println((String) map.get("value"));
        }

    方法重载与方法签名

    方法重载要求方法具备不同的特征签名,返回值不包含在方法的特征签名中,所以返回值不参与重载选择。但是Java语言和字节码中,方法签名的标准不同,Java代码中的方法签名只包含方法名称、参数顺序及参数类型,而在字节码中的方法签名还包括返回值及受查异常表部分。

    展开全文
  • java泛型的类型擦除

    2018-11-07 12:50:22
    java泛型的类型擦除 泛型是 Java 1.5 版本才引进的概念,在这之前是没有泛型的概念的,但显然,泛型代码能够很好地之前版本的代码很好地兼容。 因为,泛型的信息只存在于代码编译阶段,在进入 JVM 之前,与泛型...
  • 泛型擦除 Java 泛型的参数只可以代表,不能代表个别对象。由于 Java 泛型的类型参数之实际类型在编译时会被消除,所以无法在运行时得知其类型参数的类型。Java 编译器在编译泛型时会自动加入类型转换的编码,故...
  • Java泛型:类型擦除

    2018-12-29 20:53:39
    Java泛型:泛型、泛型接口泛型方法 类型擦除 代码片段一 1 2 3 4 5 6 7 Class c1 = new ArrayList&lt;Integer&gt;().getClass(); Class c2 = new ArrayList&lt;String&...
  • 泛型和类型擦除泛型,一个孤独的守门者。大家可能会有疑问,我为什么叫做泛型是一个守门者。这其实是我个人的看法而已,我的意思是说泛型没有其看起来那么深不可测,它并不神秘与神奇...
  • Java泛型-类型擦除

    2016-09-26 15:37:04
    Java泛型-类型擦除 一、概述 Java泛型在使用过程有诸多的问题,如不存在List.class, List不能赋值给List(不可协变),...Java泛型的处理几乎都在编译器中进行,编译器生成的bytecode是不包涵泛型信息的,泛型
  • java 泛型原理 类型擦除

    万次阅读 2019-11-13 17:04:58
    1.Java泛型的实现方法:类型擦除 大家都知道,Java的泛型是伪泛型,这是因为Java在编译期间,所有的泛型信息都会被擦掉,正确理解泛型概念的首要前提是理解类型擦除。Java的泛型基本上都是在编译器这个层次上实现的...
  • Java泛型-类型擦除

    2017-07-14 22:02:28
    Java泛型-类型擦除 一、概述  Java泛型在使用过程有诸多的问题,如不存在List.class, List不能赋值给List(不可协变),奇怪的ClassCastException等。 正确的使用Java泛型需要深入的了解Java的一些概念,如协...
  • Java泛型是伪泛型,这是因为Java在编译期间,所有的泛型信息都会被擦掉,正确理解泛型概念的首要前提是理解类型擦除Java泛型基本上都是在编译器这个层次上实现的,在生成的字节码中是不包含泛型中的类型信息的...
  • java泛型-类型擦除

    2016-08-10 15:30:49
    java 泛型 擦除
  • Java泛型与类型擦除

    2020-07-14 21:32:10
    java泛型使用类型擦除来实现,在编译时删掉泛型声明,使所有T声明等价于Object,加入强转,运行时无发获取泛型的类型 测试: public class c01<T extends Reader> { void bar(String s){} void foo(){ ...
  • java泛型泛型擦除

    2016-08-29 15:16:16
    与其他语言不同,java泛型被称为伪泛型。它只在程序的源码中存在,在编译后的字节码文件中,泛型被转化 成了原始类型(Raw Type)。因此对于运行期的Java语言来说,ArrayList&lt;int&gt;与ArrayList&...
  • java泛型--擦除

    2018-11-04 16:24:01
    java泛型是一种伪泛型,对于泛型参数T,如果没有边界则擦除到Object,如果有边界则擦除到第一个边界。对于泛型类型则擦除为原生类型。 一个体现类型擦除的例子 /* * 一个利用反射产生数组的。 */ class ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 19,537
精华内容 7,814
关键字:

java的泛型和类的擦除

java 订阅