泛型-类型擦除-Type

读《深入理解java虚拟机》的时候读到泛型和类型擦除。经过javac编译后泛型类型都会变为原生类型,文中还指出,泛型技术其实是一种伪泛型,List<String> 和 List<Integer>在字节码中都是List,Java中称为类型擦除,但是在类、方法表、字段表中加入了Signature属性用以记录了泛型的信息。

读完后大感惊讶,我原来以为,List<String> 和 List<Integer>是两个完全不同的对象,然而在编译阶段就被统一打成了List.....只是加了个签名记录。由此,翻了翻源码找寻答案。

泛型和类型擦除

java代码,创建List<String>和List<Integer>

public class TypeTest {

    public static void main(String[] args) {
        List<String> strings = new ArrayList<String>();
        List<Integer> integers = new ArrayList();
    }
}

class文件反编译后的代码,泛型的确被擦除了。

public class TypeTest {
    public TypeTest() {
    }

    public static void main(String[] args) {
        new ArrayList();
        new ArrayList();
    }
}

查看code中的字节码指令:javap -c TypeTest 字节码中的说明新建的对象的确是java/util/ArrayList这个类型,并未发现泛型的踪迹。

Compiled from "TypeTest.java"
public class sourcecode.TypeTest {
  public sourcecode.TypeTest();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class java/util/ArrayList
       3: dup
       4: invokespecial #3                  // Method java/util/ArrayList."<init>":()V
       7: astore_1
       8: new           #2                  // class java/util/ArrayList
      11: dup
      12: invokespecial #3                  // Method java/util/ArrayList."<init>":()V
      15: astore_2
      16: return
}



打印Class文件字节码内容 javap -verbose TypeTest

Classfile /D:/algorithm/target/classes/sourcecode/TypeTest.class
  Last modified 2017-9-14; size 635 bytes
  MD5 checksum 00c2ab23b321a7bd4a5bdaaf5a125d8c
  Compiled from "TypeTest.java"
public class sourcecode.TypeTest
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #5.#25         // java/lang/Object."<init>":()V
   #2 = Class              #26            // java/util/ArrayList
   #3 = Methodref          #2.#25         // java/util/ArrayList."<init>":()V
   #4 = Class              #27            // sourcecode/TypeTest
   #5 = Class              #28            // java/lang/Object
   #6 = Utf8               <init>
   #7 = Utf8               ()V
   #8 = Utf8               Code
   #9 = Utf8               LineNumberTable
  #10 = Utf8               LocalVariableTable
  #11 = Utf8               this
  #12 = Utf8               Lsourcecode/TypeTest;
  #13 = Utf8               main
  #14 = Utf8               ([Ljava/lang/String;)V
  #15 = Utf8               args
  #16 = Utf8               [Ljava/lang/String;
  #17 = Utf8               strings
  #18 = Utf8               Ljava/util/List;
  #19 = Utf8               integers
  #20 = Utf8               LocalVariableTypeTable
  #21 = Utf8               Ljava/util/List<Ljava/lang/String;>;
  #22 = Utf8               Ljava/util/List<Ljava/lang/Integer;>;
  #23 = Utf8               SourceFile
  #24 = Utf8               TypeTest.java
  #25 = NameAndType        #6:#7          // "<init>":()V
  #26 = Utf8               java/util/ArrayList
  #27 = Utf8               sourcecode/TypeTest
  #28 = Utf8               java/lang/Object
{
  public sourcecode.TypeTest();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 9: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lsourcecode/TypeTest;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: new           #2                  // class java/util/ArrayList
         3: dup
         4: invokespecial #3                  // Method java/util/ArrayList."<init>":()V
         7: astore_1
         8: new           #2                  // class java/util/ArrayList
        11: dup
        12: invokespecial #3                  // Method java/util/ArrayList."<init>":()V
        15: astore_2
        16: return
      LineNumberTable:
        line 13: 0
        line 15: 8
        line 18: 16
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      17     0  args   [Ljava/lang/String;
            8       9     1 strings   Ljava/util/List;
           16       1     2 integers   Ljava/util/List;
      LocalVariableTypeTable:
        Start  Length  Slot  Name   Signature
            8       9     1 strings   Ljava/util/List<Ljava/lang/String;>;
           16       1     2 integers   Ljava/util/List<Ljava/lang/Integer;>;
}
SourceFile: "TypeTest.java"


在Code属性下,LocalVariableTypeTable属性中存在Signature属性,其中明显的指出strings和integers这两个对象的泛型。

java1.5因出现泛型而随之出现的就是Type接口及其子接口GenericArrayType(泛型数组), ParameterizedType(参数化类型), TypeVariable(类型变量), WildcardType(通配符类型), 用以表示扩展的类型。

测试如下:


public class TypeTest {


    List<Integer> integers;

    @Test
    public void test() throws NoSuchFieldException {

        //获取integers字段
        Field field = TypeTest.class.getDeclaredField("integers");

        //获取integers字段的类型Type
        System.out.println("Type:" + field.getType());//Type:interface java.util.List

        System.out.println(field.getType() instanceof Type);//true



        //获取integers字段的泛型
        System.out.println("GenericType:" + field.getGenericType());//GenericType:java.util.List<java.lang.Integer>

        //integers字段的泛型是否是一种Type
        System.out.println(field.getGenericType() instanceof Type);//true

        //integers字段的泛型是否是ParameterizedType的实例
        System.out.println(field.getGenericType() instanceof ParameterizedType);//true

        //integers字段的泛型是否是GenericArrayType的实例
        System.out.println(field.getGenericType() instanceof GenericArrayType);//false


        //integers字段的泛型是否是TypeVariable的实例
        System.out.println(field.getGenericType() instanceof TypeVariable);//false

        //integers字段的泛型是否是WildcardType的实例
        System.out.println(field.getGenericType() instanceof WildcardType);//false
    }

    
}
    

integers首先是个List,而List中存放的是Integer类型的数据,泛型的作用是明确内层参数的类型,也可以说所需参数的类型。从test()测试方法中可以看出,integers字段的中Type属于通用级别,且只表示最外层类型,或者说主类型。

integers的泛型属于ParameterizedType -参数化类型。

另附上Field部分代码

public final class Field extends AccessibleObject implements Member {

    private Class<?>            type;
    // Generics and annotations support  --明确注释了 为了支持泛型和注解
    private transient String    signature;
    // generic info repository; lazily initialized --泛型信息仓库,延迟初始化,不调用时为null
    private transient FieldRepository genericInfo;
    
    
    private String getGenericSignature() {return signature;}

    
    /**
     * Returns a {@code Class} object that identifies the
     * declared type for the field represented by this
     * {@code Field} object.
     *返回字段声明类型的对象
     */
    public Class<?> getType() {
        return type;
    }
    
    /**
     * Returns a {@code Type} object that represents the declared type for
     * the field represented by this {@code Field} object.
     *
     * <p>If the {@code Type} is a parameterized type, the
     * {@code Type} object returned must accurately reflect the
     * actual type parameters used in the source code.
     *  如果该类型时参数化的类型,返回的类型对象必须反映源代码中使用的实际类型参数
     * <p>If the type of the underlying field is a type variable or a
     * parameterized type, it is created. Otherwise, it is resolved.
     * 如果基础字段的类型是类型变量或参数化类型,则会创建它。否则解析它。
     * @since 1.5省略异常说明
     */
    public Type getGenericType() {
    //泛型签名如果不为空,返回getGenericInfo().getGenericType()方法解析singture字段。
    //怎么解析和转换的就不多说了
        if (getGenericSignature() != null)
            return getGenericInfo().getGenericType();
        else
        //泛型为空,则返回Type类型
            return getType();
    }

image 调试可知,其signature的确是字节码中Signature属性- Ljava/util/List<Ljava/lang/Integer;>;

= =说好的reflect包源码呢...下次说type

转载于:https://my.oschina.net/u/2500836/blog/1538177

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值