`
agapple
  • 浏览: 1597873 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

java泛型type体系整理

    博客分类:
  • java
阅读更多

一直对jdk的ref使用比较模糊,早上花了点时间简单的整理了下,也帮助自己理解一下泛型的一些处理。

 

java中class,method,field的继承体系

 

 

java中所有对象的类型定义类Type


 

说明:

   Type :  Type is the common superinterface for all types in the Java programming language. These include raw types, parameterized types, array types, type variables and primitive types.

 

使用

  一般我们不直接操作Type类型,所以第一次使用会对这个比较陌生,相对内部的一些概念。

 

  根据Type类型分类,整理了一个type -> class的转换过程,同理也包括处理Generic Type。支持多级泛型处理。

 

 

private static Class<?> getClass(Type type, int i) {
        if (type instanceof ParameterizedType) { // 处理泛型类型
            return getGenericClass((ParameterizedType) type, i);
        } else if (type instanceof TypeVariable) {
            return (Class<?>) getClass(((TypeVariable) type).getBounds()[0], 0); // 处理泛型擦拭对象<R>
        } else {// class本身也是type,强制转型
            return (Class<?>) type;
        }
    }

    private static Class<?> getGenericClass(ParameterizedType parameterizedType, int i) {
        Object genericClass = parameterizedType.getActualTypeArguments()[i];
        if (genericClass instanceof ParameterizedType) { // 处理多级泛型
            return (Class<?>) ((ParameterizedType) genericClass).getRawType();
        } else if (genericClass instanceof GenericArrayType) { // 处理数组泛型
            return (Class<?>) ((GenericArrayType) genericClass).getGenericComponentType();
        } else if (genericClass instanceof TypeVariable) { // 处理泛型擦拭对象<R>
            return (Class<?>) getClass(((TypeVariable) genericClass).getBounds()[0], 0);
        } else {
            return (Class<?>) genericClass;
        }
    }

 

 

测试代码: 

 

 

interface GeneircInteface<T> {

    T method1(T obj);
}

interface CommonInteface {

    Integer method2(Integer obj);
}

class BaseGeneircInteface<R> implements GeneircInteface<R> {

    protected R result;

    @Override
    public R method1(R obj) {
        return obj;
    }

}

class GenericClass extends BaseGeneircInteface<List<String>> implements GeneircInteface<List<String>>, CommonInteface {

    @Override
    public List<String> method1(List<String> obj) {
        result = obj;
        return result;
    }

    public Integer method2(Integer obj) {
        return obj;
    }

    public <T, E extends Throwable> T method3(T obj) throws E {
        return obj;
    }

}
  

针对class的泛型接口使用:

 

private static void classGeneric() {
        System.out.println("\n--------------------- classGeneric ---------------------");
        GenericClass gc = new GenericClass();
        Type[] gis = gc.getClass().getGenericInterfaces(); // 接口的泛型信息
        Type gps = gc.getClass().getGenericSuperclass(); // 父类的泛型信息
        TypeVariable<?>[] gtr = gc.getClass().getTypeParameters(); // 当前接口的参数信息
        System.out.println("============== getGenericInterfaces");
        for (Type t : gis) {
            System.out.println(t + " : " + getClass(t, 0));
        }
        System.out.println("============== getGenericSuperclass");
        System.out.println(getClass(gps, 0));
        System.out.println("============== getTypeParameters");
        for (TypeVariable t : gtr) {
            StringBuilder stb = new StringBuilder();
            for (Type tp : t.getBounds()) {
                stb.append(tp + " : ");
            }

            System.out.println(t + " : " + t.getName() + " : " + stb);
        }

    }
 

 

针对method的泛型接口使用:

 

private static void methodGeneric() throws Exception {
        System.out.println("\n--------------------- methodGeneric ---------------------");
        GenericClass gc = new GenericClass();
        Method method3 = gc.getClass().getDeclaredMethod("method3", new Class[] { Object.class });

        Type[] gpt3 = method3.getGenericParameterTypes();
        Type[] get3 = method3.getGenericExceptionTypes();
        Type gt3 = method3.getGenericReturnType();
        System.out.println("============== getGenericParameterTypes");
        for (Type t : gpt3) {
            System.out.println(t + " : " + getClass(t, 0));
        }
        System.out.println("============== getGenericExceptionTypes");
        for (Type t : get3) {
            System.out.println(t + " : " + getClass(t, 0));
        }
        System.out.println("============== getType");
        System.out.println(gt3 + " : " + getClass(gt3, 0));
    }

 

 

针对field的泛型接口使用:

 

private static void fieldGeneric() throws Exception {
        System.out.println("\n--------------------- fieldGeneric ---------------------");
        GenericClass gc = new GenericClass();
        Field field = gc.getClass().getSuperclass().getDeclaredField("result");

        Type gt = field.getGenericType();
        Type ft = field.getType();
        System.out.println("============== getGenericType");
        System.out.println(gt + " : " + getClass(gt, 0));
        System.out.println("============== getType");
        System.out.println(ft + " : " + getClass(ft, 0));
    }

 

输出结果:

 

--------------------- classGeneric ---------------------
============== getGenericInterfaces
com.agapple.misc.GeneircInteface<java.util.List<java.lang.String>> : interface java.util.List
interface com.agapple.misc.CommonInteface : interface com.agapple.misc.CommonInteface
============== getGenericSuperclass
interface java.util.List
============== getTypeParameters

--------------------- fieldGeneric ---------------------
============== getGenericType
R : class java.lang.Object
============== getType
class java.lang.Object : class java.lang.Object

--------------------- methodGeneric ---------------------
============== getGenericParameterTypes
T : class java.lang.Object
============== getGenericExceptionTypes
E : class java.lang.Throwable
============== getType
T : class java.lang.Object

 

结果说明:

 

  1. 因为泛型的擦拭,对应的GeneircInteface和BaseGeneircInteface,在源码信息已被擦除对应的类型,进行了upper转型,所以取到的是Object。可以使用extends
  2. GenericClass在类定义时,声明了继承父接口的泛型为List,所以再通过接口和父类获取泛型信息时,是能正确的获取。通过javap -v可以获取对应的class信息
    const #46 = Asciz	Lcom/agapple/misc/BaseGeneircInteface<Ljava/util/List<Ljava/lang/String;>;>;Lcom/agapple/misc/GeneircInteface<Ljava/util/List<Ljava/lang/String;>;>;Lcom/agapple/misc/CommonInteface;;
  3. 而在GenericClass中定义的方法method3,在class信息是一个被向上转型后擦拭的信息。所以获取method3的相关泛型信息是没有的。
    method3;
    const #36 = Asciz	(Ljava/lang/Object;)Ljava/lang/Object;;
    const #37 = Asciz	Exceptions;
    const #38 = class	#39;	//  java/lang/Throwable
    const #39 = Asciz	java/lang/Throwable;
    const #40 = Asciz	<T:Ljava/lang/Object;E:Ljava/lang/Throwable;>(TT;)TT;^TE;;
    const #41 = Asciz	TT;;
思考问题:
  1. List<String> list = new ArrayList<String>();  是否有获取对应的String泛型信息?  不能,临时变量不能保存泛型信息到具体class对象中,List<String>和List<Number>对应的class实体是同一个。
  2. GeneircInteface gi = new GeneircInteface<Integer>() {
    
                @Override
                public Integer method1(Integer obj) {
                    return 1;
                }
    
            };
     通过匿名类的方式,是否可以获取Integer的泛型信息?  能,匿名类也会在进行class compiler保存泛型信息。
  3. 假如本文例子中的method3,是放在父类中BaseGeneircInteface中进行申明,GenericClass中指定R为List<String>,是否可以获取到对应的泛型信息?  不能,理由和问题1类似。

备注

具体泛型擦拭和信息保存,引用了撒迦的一段回复,解释的挺详尽了。

 

RednaxelaFX 写道

Java泛型有这么一种规律:
位于声明一侧的,源码里写了什么到运行时就能看到什么;
位于使用一侧的,源码里写什么到运行时都没了。

什么意思呢?“声明一侧”包括泛型类型(泛型类与泛型接口)声明、带有泛型参数的方法和域的声明。注意局部变量的声明不算在内,那个属于“使用”一侧。

import java.util.List; 
import java.util.Map; 

public class GenericClass<T> { // 1 
private List<T> list; // 2 
private Map<String, T> map; // 3 

public <U> U genericMethod(Map<T, U> m) { // 4 
return null; 
} 
} 
 
上面代码里,带有注释的行里的泛型信息在运行时都还能获取到,原则是源码里写了什么运行时就能得到什么。针对1的GenericClass<T>,运行时通过Class.getTypeParameters()方法得到的数组可以获取那个“T”;同理,2的T、3的java.lang.String与T、4的T与U都可以获得。

这是因为从Java 5开始class文件的格式有了调整,规定这些泛型信息要写到class文件中。以上面的map为例,通过javap来看它的元数据可以看到记录了这样的信息:
private java.util.Map map; 
Signature: Ljava/util/Map; 
Signature: length = 0x2 
00 0A 

乍一看,private java.util.Map map;不正好显示了它的泛型类型被擦除了么?
但仔细看会发现有两个Signature,下面的一个有两字节的数据,0x0A。到常量池找到0x0A对应的项,是:
const #10 = Asciz Ljava/util/Map<Ljava/lang/String;TT;>;; 
 
也就是内容为“Ljava/util/Map<Ljava/lang/String;TT;>;”的一个字符串。
根据Java 5开始的新class文件格式规范,方法与域的描述符增添了对泛型信息的记录,用一对尖括号包围泛型参数,其中普通的引用类型用“La/b/c/D;”的格式记录,未绑定值的泛型变量用“Txxx;”的格式记录,其中xxx就是源码中声明的泛型变量名。类型声明的泛型信息也以类似下面的方式记了下来:

public class GenericClass extends java.lang.Object 
Signature: length = 0x2 
00 12 
// ... 
const #18 = Asciz <T:Ljava/lang/Object;>Ljava/lang/Object;; 
 

详细信息请参考官方文档:http://java.sun.com/docs/books/jvms/second_edition/ClassFileFormat-Java5.pdf


相比之下,“使用一侧”的泛型信息则完全没有被保留下来,在Java源码编译到class文件后就确实丢失了。也就是说,在方法体内的泛型局部变量、泛型方法调用之类的泛型信息编译后都消失了。

import java.util.ArrayList; 
import java.util.List; 

public class TestClass { 
public static void main(String[] args) { 
List<String> list = null; // 1 
list = new ArrayList<String>(); // 2 
for (int i = 0; i < 10; i++) ; 
} 
} 
 

上面代码中,1留下的痕迹是:main()方法的StackMapTable属性里可以看到:

StackMapTable: number_of_entries = 2 
frame_type = 253 /* append */ 
offset_delta = 12 
locals = [ class java/util/List, int ] 
frame_type = 250 /* chop */ 
offset_delta = 11 
 

但这里是没有留下泛型信息的。这段代码只所以写了个空的for循环就是为了迫使javac生成那个StackMapTable,让1多留个影。
如果main()里用到了list的方法,那么那些方法调用点上也会留下1的痕迹,例如如果调用list.add("");,则会留下“java/util/List.add:(Ljava/lang/Object;)Z”这种记录。
2留下的是“java/util/ArrayList."<init>":()V”,同样也丢失了泛型信息。

由上述讨论可知,想对带有未绑定的泛型变量的泛型类型获取其实际类型是不现实的,因为class文件里根本没记录实际类型的信息。觉得这句话太拗口的话用例子来理解:要想对java.util.List<E>获取E的实际类型是不现实的,因为List.class文件里只记录了E,却没记录使用List<E>时E的实际类型。
想对局部变量等“使用一侧”的已绑定的泛型类型获取其实际类型也不现实,同样是因为class文件中根本没记录这个信息。例子直接看上面讲“使用一侧”的就可以了。

知道了什么信息有记录,什么信息没有记录之后,也就可以省点力气不去纠结“拿不到T的实际类型”、“建不出T类型的数组”之类的问题了orz
  • 大小: 6.5 KB
  • 大小: 21.5 KB
2
2
分享到:
评论

相关推荐

    JAVA泛型加减乘除

    这是一个使用JAVA实现的泛型编程,分为两部分,第一部分创建泛型类,并实例化泛型对象,得出相加结果。 第二部分用户自行输入0--4,选择要进行的加减乘除运算或退出,再输入要进行运算的两个数,并返回运算结果及...

    Java泛型的用法及T.class的获取过程解析

    Java泛型的用法及T.class的获取过程解析 Java泛型是Java编程语言中的一种重要特性,它允许开发者在编写代码时指定类型参数,从而提高代码的灵活性和可读性。本文将详细介绍Java泛型的用法 及T.class的获取过程解析...

    很好的Java泛型的总结

    Java泛型机制详解 Java泛型是Java语言中的一种机制,用于在编译期检查类型安全。Java泛型的出现解决了Java早期版本中类型安全检查的缺陷。Java泛型的好处是可以在编译期检查类型安全,避免了运行时的...

    Java泛型应用实例

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

    Java泛型三篇文章,让你彻底理解泛型(super ,extend等区别)

    Java 泛型详解 Java 泛型是 Java SE 5.0 中引入的一项特征,它允许程序员在编译时检查类型安全,从而减少了 runtime 错误的可能性。泛型的主要优点是可以Reusable Code,让程序员编写更加灵活和可维护的代码。 ...

    1.java泛型定义.zip

    1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1....

    java泛型技术之发展

    4. 普及与优化:随着Java泛型的广泛应用,JDK 6和7对其进行了进一步优化,如类型推断(Type Inference),使得编写泛型代码更加简洁。 二、核心概念 1. 泛型类:可以带有类型参数的类,例如`ArrayList&lt;T&gt;`,这里的...

    java 泛型接口示例

    Java 泛型是Java SE 5.0引入的一项重要特性,极大地增强了代码的类型安全性和重用性。泛型接口是泛型在接口中的应用,它允许我们在接口中定义带有类型参数的方法,使得实现该接口的类可以使用不同的数据类型。下面...

    4.java泛型的限制.zip

    4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip...

    java 泛型类的类型识别示例

    在Java编程语言中,泛型(Generics)是一种强大的特性,它允许我们在编写代码时指定容器(如集合)可以存储的数据类型。这提高了代码的安全性和效率,因为编译器可以在编译时检查类型,避免了运行时...

    java 泛型方法使用示例

    Java 泛型是Java SE 5.0引入的一项重要特性,极大地增强了代码的类型安全性和重用性。泛型方法是泛型技术在类方法层面的应用,它允许我们定义一个可以处理多种数据类型的通用方法。下面我们将深入探讨Java泛型方法的...

    java泛型学习ppt

    "Java 泛型学习" Java 泛型是 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类。泛型的主要目标是提高 Java 程序的类型安全。通过知道使用泛型定义的变量的类型限制,编译器可以在一个高得多的...

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

    Java泛型是Java编程语言中的一个强大特性,它允许在定义类、接口和方法时使用类型参数,从而实现参数化类型。这使得代码更加安全、可读性更强,并且能够减少类型转换的必要。在“java泛型的内部原理及更深应用”这个...

    java泛型详解.pdf

    java泛型详解.pdf

    java泛型的知识,网上搜集整理,包括阐述,例子,以及心得

    java泛型 开发心得 实例代码 详解文档 全是从网上搜索得来的,感觉比较好的

    SUN公司Java泛型编程文档

    Java泛型是Java编程语言中的一个关键特性,它在2004年随着JDK 5.0的发布被引入。这个特性极大地提高了代码的类型安全性和可读性,减少了在运行时出现ClassCastException的可能性。SUN公司的Java泛型编程文档,包括...

Global site tag (gtag.js) - Google Analytics