转载请注明出处:https://my.oschina.net/u/874727/blog/747427java
Q:1025250620数组
在不少Java的开源项目中都用到Java的泛型。好比Gson,就能够经过TypeToken<T>里的泛型参数来指定生成的类型。鉴于网上关于泛型的文章并很少,为了非墨后面项目研究的须要,非墨开始研究这部分的API。首先咱们先来看一下在Java语言中泛型的例子:bash
public class MyTest<T1,T2 extends Number> { T1 member; public <T> void method(T m) {} }
上述代码中的标志:T1,T2,T都是泛型类型。Java的泛型检查发生在编译期,可是会在编译后的JVM字节码中增长类型判断的语句。为了方便你们理解这句话咱们用一段代码测试一下:测试
List<String> list = new ArrayList<>(); try { Method m = list.getClass().getDeclaredMethod("add", new Class[]{Object.class}); m.invoke(list, 1); m.invoke(list, 2); } catch (Exception e) { System.out.println("error"); }
在上述代码中,虽然List变量指定了内部元素的类型String,可是在JVM运行期间list对象的add方法调用的仍是add(Ljava/lang/Object)签名的方法。因此此段代码执行之后,控制台不会有任何的输出。基于此段代码的基础上,咱们增长另外一段验证代码:.net
for (Object o:list) { System.out.println(">>"+o); }//println >>1 >>2 //will error System.out.println("--->"+list.get(0));
咱们打印list中的子元素,若是子元素类型是Object类型的话代码正常运行。可是若是咱们直接经过list.get的方式来获取元素的时候程序就会抛出一个Class Cast异常。这是为何呢?咱们来看一下编译后的JVM字节码:scala
95: iconst_0 96: invokeinterface #56, 2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object; 101: checkcast #62; //class java/lang/String
咱们看到,Java编译器在执行段后插入了"checkcast"指令。也就是说Java的泛型只不过是在执行期间增长了一些类型的检查语句。可是,尽管泛型发生在编译器,可是java仍是把类型记录在类型对象中。这就是咱们今天讨论的主角Type对象。咱们看一下Type类型的继承树:code
能够看出,Type类型有四个直接接口子类,一个实现类。此外还有另一个接口GenericDeclaration。这个接口注明哪一个类能够声明泛型。按照咱们经过第一代码块能够知道,在Java语言中,能够声明泛型的是类,方法。为何没有成员变量呢?咱们在第一个代码块中的"T1 member"不也是泛型声明么?对象
或许这里咱们应该换一个说法,对于member变量来讲,它只是使用了泛型而并非声明了泛型,声明T1泛型的是MyTest类。咱们把构造器也当作方法的一部分,方法做为可执行提,在方法和构造器类的基础上JAVA又作了一层抽象--Executable类。blog
Type类型的四个直接子类注释里已经给出了解释。因为这些东西在javadoc中也说的并不详细,也很难用辞藻把他描述的很是清楚。为了让你们理解,非墨用一些简单的代码来让你们更加直观的理解他们是个什么东东。咱们先将可能涉及到的泛型类型和声明方式都写到一个类中:继承
public static class TypeClazz<T1, T2 extends Number> { public T2 member; public T1 member2; public Collection<? extends Number> collection; public Collection<T2> collection2; public T2[] array; public <T extends Type> void method(T p1,T2 p2) {} }
这个类基本涵盖了全部的泛型状况。咱们还须要增长一些方法来打印咱们所关心的信息:
public static Type printlnFieldType(String name) { System.out.println("name:"+name); Class clazzType = TypeClazz.class; Type type = null; try { Field field = clazzType.getDeclaredField(name); type = field.getGenericType(); printlnType(field.getGenericType()); } catch (Exception e) {} if (type instanceof ParameterizedType) { ParameterizedType ptype = (ParameterizedType)type; Type[] types = ptype.getActualTypeArguments(); for (Type t:types) { System.out.print(">>"); printlnType(t); } } return type; } public static void printlnMethodReturnType(String name) { System.out.println("name:"+name); Class clazzType = TypeClazz.class; try { Method[] ms = clazzType.getDeclaredMethods(); Method method = null; for (Method m:ms) { if(m.getName().equals(name)) { method = m; break; } } printlnType(method.getGenericReturnType()); } catch (Exception e) { e.printStackTrace(); } } public static void printlnMethodParamTypes(String name) { System.out.println("name:"+name); Class clazzType = TypeClazz.class; try { Method[] ms = clazzType.getDeclaredMethods(); Method method = null; for (Method m:ms) { if(m.getName().equals(name)) { method = m; break; } } Type[] types = method.getGenericParameterTypes(); for (Type t:types) { printlnType(t); } } catch (Exception e) { e.printStackTrace(); } }
对于属性,咱们只关心它的类型,而对于方法,咱们不只须要关心它的参数类型,还要关心它的返回类型。咱们来调用一下以上的信息:
public static void main(String ...args) { printlnFieldType("member"); printlnFieldType("member2"); printlnFieldType("collection"); printlnFieldType("collection2"); printlnFieldType("array"); printlnMethodReturnType("method"); printlnMethodParamTypes("method"); }
控制台输出:
name:member >>>TypeVariable name:member2 >>>TypeVariable name:collection >>>ParameterizedType >>>>>WildcardType name:collection2 >>>ParameterizedType >>>>>TypeVariable name:array >>>GenericArrayType name:method >>>class name:method >>>TypeVariable >>>TypeVariable
对于直接采用泛型方式定义的member来讲,它都是TypeVariable类型。而对于包含有泛型定义的collection来讲,它属于参数化的ParameterizedType类型。因为数组类型是单独的类型,而且由虚拟机动态生成,所以,Type子类中有专门针对数组的GenericArrayType类型。例子中的array成员就是这种类型。咱们揭开了collection的面纱后,泛型定义为"<? extends Number>"。这个就是通配符泛型WildcardType。
相信以上的例子已经很能解释这四种类型的定义了,下一篇,非墨将带着这些基础知识,深刻到一个开源项目的源码中,看下别人的项目是如何巧妙的运用这点的。