在使用Doclet的过程中发现一个问题:无法获得泛型类型信息。为什么呢?明明模式的Doclet生成的HTML页面中,泛型信息是被正确展示的。
比如对于这样的一个字段:
1 public List<String> field;
获得这个字段的doc对象,并获取type,这是符合直觉的做法了:
1 2 Type type = field.type(); System.out.println(type); // 输出 java.util.List
发现type的toString信息里面没有包含泛型信息,是显示了List类。
观察Type类,发现一个方法:asParameterizedType
,注释说明如果这个类型是泛型类或者接口,这个方法会返回ParameterizedType,其他类型,返回null。我试了一下:
1 2 ParameterizedType parameterizedType = type.asParameterizedType(); System.out.println(parameterizedType); // 输出 java.util.List
日了狗,怎么还是没有包含泛型信息???
百思不得其解,最后还是Stack Overflow给我答案:
回答指出需要在doclet类中添加:
1 2 3 4 5 6 7 8 /** * NOTE: Without this method present and returning LanguageVersion.JAVA_1_5, * Javadoc will not process generics because it assumes LanguageVersion.JAVA_1_1 * @return language version (hard coded to LanguageVersion.JAVA_1_5) */ public static LanguageVersion languageVersion() { return LanguageVersion.JAVA_1_5; }
如果不指定这个回调函数,java doclet会默认使用java1.1的逻辑来处理,所以就不处理泛型信息了。
搞定。
这个LanguageVersion类竟然只有两个枚举:
1 2 3 4 5 6 7 8 public enum LanguageVersion { /** 1.1 added nested classes and interfaces. */ JAVA_1_1, /** 1.5 added generic types, annotations, enums, and varArgs. */ JAVA_1_5 }
不得不叹服1.5做出的改变之大,边边角角都有受到影响。
同时,大家可以到http://hg.openjdk.java.net/jdk7u/jdk7u/langtools/file/f1ffea3bd4a4/src/share/classes/com/sun,这里来下载dotlet的源码。
搜索源码发现了默认设置java1.1的地方:
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 /** * Return the language version supported by this doclet. * If the method does not exist in the doclet, assume version 1.1. */ public LanguageVersion languageVersion() { try { Object retVal; String methodName = "languageVersion"; Class<?>[] paramTypes = new Class<?>[0]; Object[] params = new Object[0]; try { retVal = invoke(methodName, JAVA_1_1, paramTypes, params); } catch (DocletInvokeException exc) { return JAVA_1_1; } if (retVal instanceof LanguageVersion) { return (LanguageVersion)retVal; } else { messager.error(null, "main.must_return_languageversion", docletClassName, methodName); return JAVA_1_1; } } catch (NoClassDefFoundError ex) { // for boostrapping, no Enum class. return null; } }
doclet是通过反射来调用languageVersion这个方法。如果没有指定就设置为1.1。搞不懂为什么不弄一个doclet接口,第一需要实现的方法,搞得这么玄虚。
同时发现一个类:AbstractDoclet
,实现了一部分方法了,比如返回1.5的languageVersion。所以我们直接继承这个类就行了。