一直对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。支持多级泛型处理。
Java代码
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); // 处理泛型擦拭对象
} 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) { // 处理泛型擦拭对象
return (Class) getClass(((TypeVariable) genericClass).getBounds()[0], 0);
} else {
return (Class) genericClass;
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
测试代码:
Java代码
interface GeneircInteface {
T method1(T obj);
}
interface CommonInteface {
Integer method2(Integer obj);
}
class BaseGeneircInteface implements GeneircInteface {
protected R result;
@Override
public R method1(R obj) {
return obj;
}
}
class GenericClass extends BaseGeneircInteface>
implements GeneircInteface>, CommonInteface {
@Override
public List method1(List obj) {
result = obj;
return result;
}
public Integer method2(Integer obj) {
return obj;
}
public extends Throwable> T method3(T obj) throws E {
return obj;
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
针对class的泛型接口使用:
Java代码
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);
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
针对method的泛型接口使用:
Java代码
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));
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
针对field的泛型接口使用:
Java代码
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));
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
输出结果:
Java代码
--------------------- classGeneric ---------------------
============== getGenericInterfaces
com.agapple.misc.GeneircInteface> : 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.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
结果说明:
因为泛型的擦拭,对应的GeneircInteface和BaseGeneircInteface,在源码信息已被擦除对应的类型,进行了upper转型,所以取到的是Object。可以使用extends
GenericClass在类定义时,声明了继承父接口的泛型为List,所以再通过接口和父类获取泛型信息时,是能正确的获取。通过javap -v可以获取对应的class信息
Java代码
const #46 = Asciz Lcom/agapple/misc/BaseGeneircInteface;>;Lcom/agapple/misc/GeneircInteface;>;Lcom/agapple/misc/CommonInteface;;
- 1.
- 1.
- 2.
- 3.
而在GenericClass中定义的方法method3,在class信息是一个被向上转型后擦拭的信息。所以获取method3的相关泛型信息是没有的。
Java代码
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 (TT;)TT;^TE;;
const #41 = Asciz TT;;
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
思考问题:
List list = new ArrayList(); 是否有获取对应的String泛型信息? 不能,临时变量不能保存泛型信息到具体class对象中,List和List对应的class实体是同一个。
Java代码
GeneircInteface gi = new GeneircInteface() {
@Override
public Integer method1(Integer obj) {
return 1;
}
};
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
通过匿名类的方式,是否可以获取Integer的泛型信息? 能,匿名类也会在进行class compiler保存泛型信息。
假如本文例子中的method3,是放在父类中BaseGeneircInteface中进行申明,GenericClass中指定R为List,是否可以获取到对应的泛型信息? 不能,理由和问题1类似。
备注
具体泛型擦拭和信息保存,引用了撒迦的一段回复,解释的挺详尽了。
RednaxelaFX 写道
Java泛型有这么一种规律:
位于声明一侧的,源码里写了什么到运行时就能看到什么;
位于使用一侧的,源码里写什么到运行时都没了。
什么意思呢?“声明一侧”包括泛型类型(泛型类与泛型接口)声明、带有泛型参数的方法和域的声明。注意局部变量的声明不算在内,那个属于“使用”一侧。
Java代码
import java.util.List;
import java.util.Map;
public class GenericClass { // 1
private List list; // 2
private Map map; // 3
public U genericMethod(Map m) { // 4
return null;
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
上面代码里,带有注释的行里的泛型信息在运行时都还能获取到,原则是源码里写了什么运行时就能得到什么。针对1的GenericClass,运行时通过Class.getTypeParameters()方法得到的数组可以获取那个“T”;同理,2的T、3的java.lang.String与T、4的T与U都可以获得。
这是因为从Java 5开始class文件的格式有了调整,规定这些泛型信息要写到class文件中。以上面的map为例,通过javap来看它的元数据可以看到记录了这样的信息:
Javap代码
private java.util.Map map;
Signature: Ljava/util/Map;
Signature: length = 0x2
00 0A
- 1.
- 2.
- 3.
- 4.
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
乍一看,private java.util.Map map;不正好显示了它的泛型类型被擦除了么?
但仔细看会发现有两个Signature,下面的一个有两字节的数据,0x0A。到常量池找到0x0A对应的项,是:
Javap代码
const #10 = Asciz Ljava/util/Map;;
- 1.
- 1.
- 2.
- 3.
也就是内容为“Ljava/util/Map;”的一个字符串。
根据Java 5开始的新class文件格式规范,方法与域的描述符增添了对泛型信息的记录,用一对尖括号包围泛型参数,其中普通的引用类型用“La/b/c/D;”的格式记录,未绑定值的泛型变量用“Txxx;”的格式记录,其中xxx就是源码中声明的泛型变量名。类型声明的泛型信息也以类似下面的方式记了下来:
Javap代码
public class GenericClass extends java.lang.Object
Signature: length = 0x2
00 12
// ...
const #18 = Asciz Ljava/lang/Object;;
- 1.
- 2.
- 3.
- 4.
- 5.
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
详细信息请参考官方文档:http://java.sun.com/docs/books/jvms/second_edition/ClassFileFormat-Java5.pdf
相比之下,“使用一侧”的泛型信息则完全没有被保留下来,在Java源码编译到class文件后就确实丢失了。也就是说,在方法体内的泛型局部变量、泛型方法调用之类的泛型信息编译后都消失了。
Java代码
import java.util.ArrayList;
import java.util.List;
public class TestClass {
public static void main(String[] args) {
List list = null; // 1
list = new ArrayList(); // 2
for (int i = 0; i < 10; i++) ;
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
上面代码中,1留下的痕迹是:main()方法的StackMapTable属性里可以看到:
Java代码
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
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
但这里是没有留下泛型信息的。这段代码只所以写了个空的for循环就是为了迫使javac生成那个StackMapTable,让1多留个影。
如果main()里用到了list的方法,那么那些方法调用点上也会留下1的痕迹,例如如果调用list.add("");,则会留下“java/util/List.add:(Ljava/lang/Object;)Z”这种记录。
2留下的是“java/util/ArrayList."":()V”,同样也丢失了泛型信息。
由上述讨论可知,想对带有未绑定的泛型变量的泛型类型获取其实际类型是不现实的,因为class文件里根本没记录实际类型的信息。觉得这句话太拗口的话用例子来理解:要想对java.util.List获取E的实际类型是不现实的,因为List.class文件里只记录了E,却没记录使用List时E的实际类型。
想对局部变量等“使用一侧”的已绑定的泛型类型获取其实际类型也不现实,同样是因为class文件中根本没记录这个信息。例子直接看上面讲“使用一侧”的就可以了。
知道了什么信息有记录,什么信息没有记录之后,也就可以省点力气不去纠结“拿不到T的实际类型”、“建不出T类型的数组”之类的问题了orz
【编辑推荐】