应用类型替换深入挖掘使用泛型的程序的细节
简介:Java™ 5 泛型把详细的类型信息编码到类文件中。许多类型的工具都可以从全面的类型 信息提供的改进的数据结构中受益,但是要把这个信息变成有用的形式可能有些困难。为了让这个工作更 容易些,系列作者 Dennis Sosnoski 围绕 ASM Java 字节码操纵框架构建了一个数据结构分析程序,可 以解释泛型信息,为应用程序使用的数据的实际数据类型创建深度的视图。
类处理工具实际上就是一个把其他程序当成数据的程序,通常会修改或重新塑造目标程序,以满足某 些目的。在把程序当成数据的时候,如果构建一个关于程序自身内部数据结构的模型,协助对修改进行指 导,那么通常是有用的。可以利用反射,在第一次把目标程序的类文件装入 JVM 之后,创建这种类型的 模型。也可以用框架直接从类文件解码出数据结构信息,甚至从源代码进行解码。不论采用何种技术,目 的都是得到应用程序使用的对象之间关系的尽可能全面的视图。
Java 5 程序中的泛型信息,提供了应用程序数据结构的详细地图。泛型之前的程序,只要运行到 Java 集合类或使用无类型引用的应用程序类时,数据结构的分析就走进了死胡同。如果没有某种形式的 外部信息,就没有办法知道无类型引用链接到什么类型的对象。在使用泛型时,可以提供附加信息作为源 代码的一部分,然后编译器会把附加的引用类型信息直接整合到二进制的类文件中。利用这种内嵌的泛型 信息是建立对象之间关系的更丰富视图的关键所在。
在这个系列的前两篇文章(“反射泛型” 和 “泛型与 ASM”)中,我首先介绍了使用反射得到泛型 信息的基础知识,然后对于使用 ASM 字节码框架处理类文件的原始泛型信息作了详细介绍。在这篇文章 中,我把 ASM 技术用得更深入一点儿,在泛型类定义中使用类型替换来构建目标应用程序数据结构的增 强视图。在进入分析类所使用的实际 ASM 代码之前,我先介绍表示和组织数据结构信息时一些比较简单 的问题。
表示数据结构
在构建分析程序时的第一个问题是,定义目标程序使用的数据结构的表示形式。这必须不仅包含每个 字段值的表示,还要包含每个值的类型信息的表示。因为我想在这一期中演示泛型的解码,所以类型信息 需要包含泛型引用所使用的具体的参数类型。
清单 1 显示了我用作基本数据结构表示的类。FieldDescription 类只是个简单的数据类,容纳字段 名称、签名和字段类型的引用。正如我在 前一期 中介绍的,签名是泛型添加到类文件格式中的项目;只 有对泛型的引用才有签名。签名定义了泛型实际使用的参数类型,所以它提供了处理类型替换时需要的信 息。对于没有签名的字段,将只使用 null 值。最后,字段类型都是 TypeDescription 类的实例,如清 单 1 所示:
清单 1. 基本数据结构类
public class FieldDescription{ // every field has a name private final String m_name; // only fields that are of generic types have signatures private final String m_signature; // type only defined when parameter types are defined private final TypeDescription m_type; public FieldDescription(String name, String sig, TypeDescription type) { m_name = name; m_signature = sig; m_type = type; } public String getName() { return m_name; } public String getSignature() { return m_signature; } public TypeDescription getType() { return m_type; }}public abstract class TypeDescription{ public static final FieldDescription[] EMPTY_FIELD_ARRAY = {}; private final String m_descripTor; protected TypeDescription(String dTor) { m_descripTor = dTor; } public boolean isArray() { return false; } public TypeDescription getArrayItemType() { throw new IllegalStateException("Not an array"); } public boolean isPrimitive() { return false; } public FieldDescription[] getFields() { return EMPTY_FIELD_ARRAY; } public String getDescripTor() { return m_descripTor; } public boolean equals(Object obj) { if (obj == this) { return true; } else if (obj instanceof TypeDescription) { return m_descripTor.equals(((TypeDescription)obj).m_descripTor); } else { return false; } } public int hashCode() { return m_descripTor.hashCode(); } public abstract String toString();}
TypeDescription 类只是个抽象基类,其中定义了处理三种类型的方法:原生类型、数组和类实例。 这个基类以类型描述符的形式包含了一个值。我用于这个类的类型描述符大致符合 JVM 规范定义的类型 描述符的形式,但是有些扩展,添加了泛型具体 “版本” 的实际参数类型列表。这个扩展允许把 Ljava/util/Map; 这种形式的描述符作为类型系统的一 部分。
组织数据结构
组织数据结构信息的问题比表示信息的问题略微复杂一些。组织信息的基本问题在于数据结构经常是 递归的:一个类中可能有一个字段,这个字段引用这个类的一个实例,或者它引用的一个类可能直接或间 接地引用回到原来的类。所以,只想直接展开结构,可能会形成无限的递归循环。
我使用目录技术来处理这种递归引用。在找到类引用时,检查目标,查看这个类以前是否已经看到过 。如果是,那么目录就返回现有的描述。如果没有,则目录创建新的描述实例,并立即把描述添加到目录 (甚至在描述的完整细节可用之前就添加)。清单 2 显示了这个 TypeDirecTory 类的基本代码,现在暂 时不管它,以后会添加处理泛型的一些细节:
清单 2. TypeDirecTory 类
public class TypeDirecTory{ /** Source for binary class representations. */ private final BinaryClassLoader m_loader; /** Field list for all arrays. */ private final FieldDescription[] m_arrayFields; /** Map from descripTor or signature to type for all non-generic types, * including generics with substitutions. */ private HashMap m_typeMap = new HashMap(); ... /** * ConstrucTor. Initializes the type direcTory with descriptions of * primitive types. * * @param loader binary class loader */ public TypeDirecTory(BinaryClassLoader loader) { m_loader = loader; addType(new PrimitiveTypeDescription("B", "byte")); addType(new PrimitiveTypeDescription("C", "char")); addType(new PrimitiveTypeDescription("D", "double")); addType(new PrimitiveTypeDescription("F", "float")); TypeDescription inttype = new PrimitiveTypeDescription("I", "int"); addType(inttype); addType(new PrimitiveTypeDescription("J", "long")); addType(new PrimitiveTypeDescription("S", "short")); addType(new PrimitiveTypeDescription("V", "void")); addType(new PrimitiveTypeDescription("Z", "boolean")); m_arrayFields = new FieldDescription[] { new FieldDescription("int", null, inttype) }; } /** * Add type description to type direcTory. * * @param desc type description to add */ public void addType(TypeDescription desc) { m_typeMap.put(desc.getDescripTor(), desc); } /** * Add generic class to template direcTory. * * @param tmpl generic template to add */ public void addTemplate(GenericTemplate tmpl) { m_templateMap.put(tmpl.getDescripTor(), tmpl); } /** * Get description for the type. The type may be a primitive, an array, or a * class. If the type is a generic class, it will be treated as though all * type variables used their lower bounds. * * @param dTor type descripTor * @return type description */ public TypeDescription getTypeInstance(String dTor) { // check for an existing description TypeDescription desc = m_typeMap.get(dTor); if (desc == null) { // new description needed - must be array or class if (dTor.charAt(0) == '[') { desc = new ArrayClassDescripTor(dTor, getTypeInstance(dTor.substring(1))); } else { // parse binary class to build description byte[] byts = m_loader.getBytes(dTor); desc = new SimpleClassDescription(dTor, byts, this); } } return desc; } /** * Get description for generic class with specific type substitutions. * * @param sig field signature with type substitutions * @param types actual types used for instance (values may be *
null
if no substitution defined) * @return type description */ public TypeDescription getSignatureInstance(String sig, TypeDescription[] types) { // first check for direct match on substituted signature TypeDescription desc = (TypeDescription)m_typeMap.get(sig); if (desc == null) { // no direct match, first handle array if (sig.charAt(0) == '[') { desc = new ArrayClassDescripTor(sig, getSignatureInstance(sig.substring(1), types)); } else { ... } } return desc; } /** * Get description for signature with type mapping. * * @param sig field signature for type variables * @param tmap type mapping for variables * @return type description */ public TypeDescription getMappedSignatureInstance(String sig, HashMap tmap) { ... } ... /** * DescripTor for primitive "class." There's really nothing to record for a * primitive type except the name, so that's all this does. Because instances * only need to be created and added to the direcTory at initialization, this * is an inner class. */ private static class PrimitiveTypeDescription extends TypeDescription { private final String m_externalName; private PrimitiveTypeDescription(String iname, String xname) { super(iname); m_externalName = xname; } public boolean isPrimitive() { return true; } public String toString() { return m_externalName; } } /** * DescripTor for array class. This is just a wrapper around the descripTor * for the item type, with the same field list used for all arrays. */ private class ArrayClassDescripTor extends TypeDescription { private final TypeDescription m_itemType; protected ArrayClassDescripTor(String name, TypeDescription item) { super(name); m_itemType = item; } public boolean isArray() { return true; } public TypeDescription getArrayItemType() { return m_itemType; } public FieldDescription[] getFields() { return m_arrayFields; } public String toString() { return m_itemType + "[]"; } }}
清单 2 的代码使用内部的 PrimitiveTypeDescription 和 ArrayTypeDescription 类直接处理原生类 型和数组类型。原生类型在创建的时候就被添加到实际的目录(m_typeMap 哈希映射把类型名称或字段签 名与对应的类型描述关联起来),而数组类型则在被看到的时候添加到目录中。getTypeInstance() 方法 处理新的数组类型的添加,递归地调用本身来得到数组的项目类型的类型描述。实际的 Java 类的类型则 通过把二进制类表示传递给 SimpleClassDescription 构造函数而生成。
getSignatureInstance() 方法在结构上与 getTypeInstance() 方法很类似,但是如果在前面没有遇 到使用某个泛型类的参数类型的相同组合,它还需要处理到泛型类的类型替换。马上我就要编写实际执行 这个任务的代码,以及相关的 getMappedSignatureInstance() 代码。首先,我要处理的代码负责构建不 带指定参数类型的类的描述。
用 ASM 构建类描述
清单 3 显示了简单类描述的实际构造,所谓简单类,我指的是非泛型类或者不带指定参数类型的泛型 类。这个类的全部工作都发生在构造函数中,构造函数首先把类的新实例添加到类型目录(以处理递归, 就像在 前一节 中讨论的),然后用 ASM 处理实际的二进制类表示,构建一组字段描述。
清单 3. SimpleClassDescription 类
public class SimpleClassDescription extends TypeDescription{ private final String m_name; private final FieldDescription[] m_fields; public SimpleClassDescription(String dTor, byte[] byts, TypeDirecTory dir) { super(dTor); m_name = BinaryClassLoader.nameFromDescripTor(dTor); dir.addType(this); DescriptionBuilderVisiTor vTor = new DescriptionBuilderVisiTor(dir); ClassReader creader = new ClassReader(byts); creader.accept(vTor, true); m_fields = vTor.getFields(); } public FieldDescription[] getFields() { return m_fields; } public String toString() { return m_name; } /** * VisiTor for generating the description information for a simple class (a * non-generic class, or a generic class used without type substitution). * If the class is generic, the bounds information from the signature is * substituted for the type parameters. */ public class DescriptionBuilderVisiTor extends EmptyVisiTor { private final TypeDirecTory m_typeDirecTory; private HashMap m_typeMap; private ArrayList m_fields; private DescriptionBuilderVisiTor(TypeDirecTory dir) { m_typeDirecTory = dir; m_fields = new ArrayList(); } public void visit(int version, int Access, String name, String sig, String sname, String[] inames) { if (sig != null) { m_typeMap = new HashMap(); new SignatureReader(sig).accept(new ClassSignatureVisiTor()); } } public FieldVisiTor visitField(int Access, String name, String desc, String sig, Object value) { TypeDescription type; if (sig == null) { type = m_typeDirecTory.getTypeInstance(desc); } else { type = m_typeDirecTory.getMappedSignatureInstance(sig, m_typeMap); } m_fields.add(new FieldDescription(name, sig, type)); return super.visitField(Access, name, desc, sig, value); } private FieldDescription[] getFields() { return m_fields.toArray(new FieldDescription[m_fields.size()]); } private class ClassSignatureVisiTor extends EmptySignatureVisiTor { private String m_lastName; private boolean m_isBounded; public void visitFormalTypeParameter(String name) { m_typeMap.put(name, m_typeDirecTory.getTypeInstance("Ljava/lang/Object;")); m_lastName = name; } public SignatureVisiTor visitClassBound() { return new EmptySignatureVisiTor() { public void visitClassType(String name) { m_typeMap.put(m_lastName, m_typeDirecTory.getTypeInstance("L" + name + ';')); m_isBounded = true; } }; } public SignatureVisiTor visitInterfaceBound() { return new EmptySignatureVisiTor() { public void visitClassType(String name) { if (!m_isBounded) { m_typeMap.put(m_lastName, m_typeDirecTory.getTypeInstance(name)); } } }; } } }}
清单 3 代码中有意思的部分在于内部的 DescriptionBuilderVisiTor 类。这个类是 ASM 的 EmptyVisiTor 类的扩展,只覆盖了两个方法。覆盖的 visit() 方法处理实际的类信息,创建嵌套内部类 ClassSignatureVisiTor 的实例,处理有签名的类(即泛型类)。这个嵌套内部类的要点就在于扫描泛型 类的类型参数,找到每个泛型类的最佳上部绑定,并在包含它们的 DescriptionBuilderVisiTor 类实例 所拥有的哈希映射中设置这个绑定。DescriptionBuilderVisiTor 覆盖的 visitField() 方法只检查字段 有没有签名。如果字段没有签名,visitField() 就只用字段描述符字符串从类型目录获得类型信息。如 果字段确实 有签名,那么 visitField() 就使用类型目录查找的备选形式(接受签名和替换类型的哈希 映射)。
DescriptionBuilderVisiTor 类代码的实际效果是构建一组字段定义,与提供给 SimpleClassDescription 构造函数的类的字段匹配。如果提供的类是泛型类,那么即使为这个类提供了 最通用的参数类型定义,也会处理类的所有字段。对于类型参数定义的最简单形式,这个最通用的参数类 型就是 java.lang.Object;如果类型参数定义包含类或接口绑定(例如 public class MyClass),那么最通用的类型与指定绑定匹配。
处理类型替换
现在我们已经完成了最简单的处理部分,剩下的惟一部分是实际的类型替换处理。清单 4 显示了执行 这个处理的代码,包括 清单 2 所示的 TypeDirecTory 类中遗漏的部分和新的 GenericTemplate 类:
清单 4. 类型替换代码
public class TypeDirecTory{ ... /** Map from generic classes descripTor to generic class information. */ private HashMap m_templateMap = new HashMap(); ... /** * Get description for generic class with specific type substitutions. * * @param sig field signature with type substitutions * @param types actual types used for instance (values may be *
null
if no substitution defined) * @return type description */ public TypeDescription getSignatureInstance(String sig, TypeDescription[] types) { // first check for direct match on substituted signature TypeDescription desc = (TypeDescription)m_typeMap.get(sig); if (desc == null) { // no direct match, first handle array if (sig.charAt(0) == '[') { desc = new ArrayClassDescripTor(sig, getSignatureInstance(sig.substring(1), types)); } else { // not an array, get the generic class descripTor String dTor = sig; int split = dTor.indexOf('= 0) { dTor = dTor.substring(0, split) + ';'; } GenericTemplate gdesc = m_templateMap.get(dTor); if (gdesc == null) { byte[] byts = m_loader.getBytes(dTor); gdesc = new GenericTemplate(dTor, byts, this); } // handle type substitution to generic version of class desc = gdesc.getParameterized(sig, types); } } return desc; } /** * Get description for signature with type mapping. * * @param sig field signature for type variables * @param tmap type mapping for variables * @return type description */ public TypeDescription getMappedSignatureInstance(String sig, HashMap tmap) { SignatureDecompositionVisiTor vTor = new SignatureDecompositionVisiTor(tmap); new SignatureReader(sig).acceptType(vTor); return vTor.getDescription(); } /** * VisiTor for processing a signature with type substitutions. This uses * itself recursively to process nested signatures, substituting actual * types for any type variable references found. */ public class SignatureDecompositionVisiTor extends EmptySignatureVisiTor { private final HashMap m_parameterMap; private int m_arrayCount; private String m_className; private char m_baseClass; private ArrayList m_parameterTypes; private boolean m_isNested; private SignatureDecompositionVisiTor m_nestedVisiTor; private void reset() { m_arrayCount = 0; m_className = null; m_parameterTypes.clear(); m_isNested = false; } private SignatureDecompositionVisiTor(HashMap tmap) { m_parameterMap = tmap; m_parameterTypes = new ArrayList(); } private void checkParameter() { if (m_isNested) { m_parameterTypes.add(m_nestedVisiTor.getDescription()); m_isNested = false; } } public SignatureVisiTor visitArrayType() { m_arrayCount++; return this; } public void visitBaseType(char desc) { m_baseClass = desc; } public void visitTypeVariable(String name) { String dTor = m_parameterMap.get(name).getDescripTor(); if (dTor == null) { throw new IllegalStateException("Undefined type variable " + name); } else if (dTor.length() < 3 || dTor.charAt(0) != 'L' || dTor.charAt(dTor.length()-1) != ';') { throw new IllegalArgumentException ("Not a valid class descripTor: " + dTor); } else { m_className = dTor.substring(1, dTor.length()-1); } } public void visitClassType(String name) { m_className = name; } public SignatureVisiTor visitTypeArgument(char wildcard) { checkParameter(); if (wildcard == '=' || wildcard == '+') { if (m_nestedVisiTor == null) { m_nestedVisiTor = new SignatureDecompositionVisiTor (m_parameterMap); } else { m_nestedVisiTor.reset(); } m_isNested = true; return m_nestedVisiTor; } else { m_parameterTypes.add(null); return new EmptySignatureVisiTor(); } } public void visitTypeArgument() { checkParameter(); m_parameterTypes.add(null); } public void visitEnd() { checkParameter(); } public TypeDescription getDescription() { // create array signature prefix StringBuffer buff = new StringBuffer(); for (int i = 0; i 0) { buff.append('<'); for (int i = 0; i '); } buff.append(';'); // get actual class description if (m_parameterTypes.size() == 0) { return getTypeInstance(buff.toString()); } else { TypeDescription[] ptypes = new TypeDescription[m_parameterTypes.size()]; ptypes = m_parameterTypes.toArray(ptypes); return getSignatureInstance(buff.toString(), ptypes); } } } } ...}public class GenericTemplate{ private final String m_descripTor; private final String m_baseName; private final TypeDirecTory m_typeDirecTory; private final FieldDescription[] m_genericFields; private final String[] m_typeParameters; private final TypeDescription[] m_upperBounds; protected GenericTemplate(String dTor, byte[] byts, TypeDirecTory dir) { m_descripTor = dTor; m_baseName = BinaryClassLoader.nameFromDescripTor(dTor); m_typeDirecTory = dir; dir.addTemplate(this); DescriptionBuilderVisiTor vTor = new DescriptionBuilderVisiTor(dir); ClassReader creader = new ClassReader(byts); creader.accept(vTor, true); m_genericFields = vTor.getFields(); m_typeParameters = vTor.getTypeParameters(); m_upperBounds = vTor.getUpperBounds(); } public String getDescripTor() { return m_descripTor; } public boolean equals(Object obj) { if (obj == this) { return true; } else if (obj instanceof GenericTemplate) { return m_descripTor.equals(((GenericTemplate)obj).m_descripTor); } else { return false; } } public int hashCode() { return m_descripTor.hashCode(); } /** * Get description for parameterized type with type substitutions. * * @param sig signature including all type substitutions * @param types actual types used for instance (values may be * null
if no substitution defined) * @param tdir type direcTory * @return substituted type description */ public TypeDescription getParameterized(String sig, TypeDescription[] types) { return new ParameterizedClassDescription(sig, types); } /** * VisiTor for generating the description information for a generic class. */ public class DescriptionBuilderVisiTor extends EmptyVisiTor { private final TypeDirecTory m_typeDirecTory; private ArrayList m_fields; private ArrayList m_typeParameters; private ArrayList m_upperBounds; private DescriptionBuilderVisiTor(TypeDirecTory dir) { m_typeDirecTory = dir; m_fields = new ArrayList(); m_typeParameters = new ArrayList(); m_upperBounds = new ArrayList(); } public void visit(int version, int Access, String name, String sig, String sname, String[] inames) { if (sig != null) { new SignatureReader(sig).accept(new ClassSignatureVisiTor()); } super.visit(version, Access, name, sig, sname, inames); } public FieldVisiTor visitField(int Access, String name, String desc, String sig, Object value) { TypeDescription type = null; if (sig == null) { type = m_typeDirecTory.getTypeInstance(desc); } m_fields.add(new FieldDescription(name, sig, type)); return super.visitField(Access, name, desc, sig, value); } private FieldDescription[] getFields() { return m_fields.toArray(new FieldDescription[m_fields.size()]); } private String[] getTypeParameters() { return m_typeParameters.toArray(new String[m_typeParameters.size()]); } private TypeDescription[] getUpperBounds() { return m_upperBounds.toArray(new TypeDescription[m_upperBounds.size ()]); } private class ClassSignatureVisiTor extends EmptySignatureVisiTor { private boolean m_isBounded; public void visitFormalTypeParameter(String name) { m_typeParameters.add(name); m_upperBounds.add(m_typeDirecTory.getTypeInstance ("Ljava/lang/Object;")); } public SignatureVisiTor visitClassBound() { return new EmptySignatureVisiTor() { public void visitClassType(String name) { m_upperBounds.set(m_upperBounds.size()-1, m_typeDirecTory.getTypeInstance("L" + name + ';')); m_isBounded = true; } }; } public SignatureVisiTor visitInterfaceBound() { return new EmptySignatureVisiTor() { public void visitClassType(String name) { if (!m_isBounded) { m_upperBounds.set(m_upperBounds.size()-1, m_typeDirecTory.getTypeInstance(name)); } } }; } } } /** * Parameterized type description, with actual types substituted for all * type parameters. The actual substitution of type parameters into the * fields is done at the time of first use to avoid issues with * recursive class references. */ private class ParameterizedClassDescription extends TypeDescription { private final TypeDescription[] m_types; private final String m_name; private FieldDescription[] m_fields; /** * ConstrucTor. This creates the description for a parameterized type * with type substitutions from the generic instance. This has to add * itself to the type direcTory during construction to handle * recursive references. * * @param sig signature including all type substitutions * @param types actual types used for instance (values may be * null
if no substitution defined) * @return substituted type description */ public ParameterizedClassDescription(String sig, TypeDescription[] types) { super(sig); StringBuffer buff = new StringBuffer(); buff.append(m_baseName); buff.append('<'); for (int i = 0; i 0) { buff.append(','); } if (types[i] == null) { buff.append('*'); } else { buff.append(types[i]); } } buff.append('>'); m_name = buff.toString(); m_types = types; m_typeDirecTory.addType(this); } public FieldDescription[] getFields() { if (m_fields == null) { // make sure the number of parameter types is correct if (m_typeParameters.length != m_types.length) { throw new IllegalArgumentException("Wrong number of parameter types"); } // build substitution map for type parameters HashMap tmap = new HashMap(); for (int i = 0; i < m_typeParameters.length; i++) { TypeDescription type = m_types[i]; if (type == null) { type = m_upperBounds[i]; } tmap.put(m_typeParameters[i], type); } // handle the actual type substitutions m_fields = new FieldDescription[m_genericFields.length]; for (int i = 0; i < m_genericFields.length; i++) { FieldDescription field = m_genericFields[i]; if (field.getType() == null) { String sig = field.getSignature(); TypeDescription desc = m_typeDirecTory.getMappedSignatureInstance(sig, tmap); m_fields[i] = new FieldDescription(field.getName(), sig, desc); } else { m_fields[i] = field; } } } return m_fields; } public String toString() { return m_name; } }}
TypeDirecTory 类中第一个遗漏的部分是 m_templateMap 字段。这个哈希映射把没有参数类型的泛型 类的字段描述符字符串与这个类的 GenericTemplate 关联起来。GenericTemplate 实例包含类的基本类 型参数信息,还有为类定义的字段的数组。不是泛型引用的字段都在泛型模型定义的时候完全定义;是 泛型引用的字段则保留作为签名,留在以后进行填充。内部的 DescriptionBuilderVisiTor 类实现从泛 型类的二进制表示实际收集这个模板信息的实际 ASM 处理工作。
清单 2 的 TypeDirecTory 类遗漏的其他部分是 getSignatureInstance() 和 getMappedSignatureInstance() 方法以及后一个方法使用的内部 SignatureDecompositionVisiTor 类的 完整代码。getSignatureInstance() 方法实际地构建新的泛型类模板,然后为参数类型的具体列表实例 化这些模板。实例化过程由 GenericTemplate 的内部 ParameterizedClassDescription 类的构造函数方 法设置,但是会推迟到这个 TypeDescription 子类第一次请求字段列表的时候。
为什么要在 ParameterizedClassDescription 类中推迟替换的处理?还是因为递归的问题。如果没有 延迟处理,就会发生这样的情况:仅仅对泛型模板的基本字段定义进行处理,就会引起对这个模板的同一 版本的实例化。
TypeDirecTory 类的 getMappedSignatureInstance() 方法接受字段签名和一个映射(映射中保存类 型参数变量名称到实际参数类型的映射),这个方法使用内部 SignatureDecompositionVisiTor 类的实 例来处理签名,并在看到类型参数变量的时候执行替换。签名完全处理之后, getMappedSignatureInstance() 方法的代码调用内部类实例的 getDescription() 方法,得到与传递进 来的签名和替换匹配的实际的类型描述。
试一下
现在已经看到了完整的数据结构分析引擎,所以剩下的就是显示它的实际功效了。为了这个目的,我 要使用在 前两篇文章 中开始的代表文件目录的同一套泛型类。因为在这篇文章中我只关注这些类的字段 而不是代码,所以在清单 5 中只显示了这些类的字段定义:
清单 5. 文件目录类
public class PathDirecTory implements Iterable{ private final PairCollection m_pathPairs; ...}public class PairCollection implements Iterable{ /** Collection with first component values. */ private final ArrayList m_tValues; /** Collection with second component values. */ private final ArrayList m_uValues; ...}public class DirInfo{ private final List m_files; private final List m_direcTories; private final Date m_lastModify; ...}public class FileInfo{ private final String m_name; private final Date m_lastModify; ...}
仅仅在一个类上运行数据结构分析引擎代码,不那么有趣,因为结果只是代表另一个数据结构的数据 结构。为了生成一些有趣的输出,我编写了清单 6 中的 Datalyze 类。这个类首先分析目标类的数据结 构,然后递归地扫描目标类和所有引用的类,输出找到的类的描述信息。为了避免输出太杂乱,当碰到来 自 java.lang.* 或 sun.* 包层次结构的类时,就停止递归。清单 6 底部的输出显示了在 清单 4 中的 PathDirecTory 类上运行这个类的结果:
清单 6. 文件目录类
public abstract class Datalyze{ private static void printDescription(TypeDescription basetype) { ArrayList refs = new ArrayList(); HashSet dones = new HashSet(); refs.add(basetype); for (int i = 0; i < refs.size(); i++) { TypeDescription ref = refs.get(i); System.out.println(ref + " fields:"); FieldDescription[] fields = ref.getFields(); for (int j = 0; j 0 && !fdTor.startsWith("Ljava/lang") && !fdTor.startsWith("Lsun/")) { refs.add(ftype); dones.add(ftype); } } } } public static void main(String[] args) { BinaryClassLoader loader = new BinaryClassLoader(); loader.addPaths(System.getProperty("java.class.path")); loader.addPaths(System.getProperty("sun.boot.class.path")); TypeDirecTory tdir = new TypeDirecTory(loader); TypeDescription desc = tdir.getTypeInstance("Lcom/sosnoski/generics/PathDirecTory;"); printDescription(desc); }}com.sosnoski.generics.PathDirecTory fields: m_pathPairs of type com.sosnoski.generics.PairCollectioncom.sosnoski.generics.PairCollection fields: m_tValues of type java.util.ArrayList m_uValues of type java.util.ArrayListjava.util.ArrayList fields: serialVersionUID of type long elementData of type java.lang.String[] size of type intjava.util.ArrayList fields: serialVersionUID of type long elementData of type com.sosnoski.generics.DirInfo[] size of type intcom.sosnoski.generics.DirInfo fields: m_files of type java.util.List m_direcTories of type java.util.List m_lastModify of type java.util.Datejava.util.Date fields: gcal of type sun.util.calendar.BaseCalendar jcal of type sun.util.calendar.BaseCalendar fastTime of type long cdate of type sun.util.calendar.BaseCalendar$Date defaultCenturyStart of type int serialVersionUID of type long wtb of type java.lang.String[] ttb of type int[]
在清单 6 底部的输出中(为了格式化稍微重新调整了结构),可以看到类型替换代码的作用。类型替 换的第一个实例在输出的第 4 和第 5 行,里面指定了 ArrayList 实例中值的类型。接下来的 ArrayList 分析显示出它有一个 String[] 字段。这个输出也显示了数据结构分析的局限 性:一旦遇到 DirInfo 类中的 List 字段,就没法进一步分解了,因为 java.util.List 是接口而不是 实际的类 —— 而且只有接口的实现才有字段。当然,对本文提供的代码做些修改,也可以把同样的分析 像用于字段一样用于方法,而且这种分析方法也能包含接口。
泛型结束语
到现在,已经看到了如何用 ASM 框架实现泛型数据结构分析,还有一个快速的示例,表现了这类分析 的可能性和局限性。还有一些比较好的应用,通过在工具中整合泛型,在 Java 类之间和一些外部形式之 间进行转换,从而提供详细的数据视图。例如,我正计划在我的 JiBX 数据绑定框架的新版本绑定生成器 中,利用泛型提供的信息,在不需要用户的额外输入的情况下,填充结构的更多细节。
在三篇关于泛型的文章之后,我要换一个主题了!接下来的类处理工具包 系列中,我将介绍一个处理 Java 5 标注的工具。以前,我曾经介绍过在使用标注作为配置信息的时候出现的问题。这个工具通过允 许在运行时覆盖标注数据,试图克服这些问题。覆盖标注的能力,能不能让它们用于更多类型的应用?请 关注下个月的文章来找到答案。
本文配套源码:http://www.bianceng.net/java/201212/733.htm
让所有的愁向后飞去。请不要回头去追你因该向前奔跑,因为快乐在前方!