如何将 method.getGenericReturnType() 转换为 jvm 类型签名



如何将java.lang.reflect.Type的实例转换为jvm泛型类型签名?

Type type = method.getGenericReturnType();
String signature = toTypeSig(type);

例如,此类型:

Map<String, Map<?, ? super Integer>>

必须成为此字符串:

"Ljava/util/Map<Ljava/lang/String;Ljava/util/Map<*-Ljava/lang/Integer;>;>;"

人们如何使用orb.objectweb.asm.ClassWriter解决这个问题?

我已经遍历了类型信息。但我更喜欢更强大的东西。(我已经发布了我自己的解决方案作为答案,因为它有点长)

我想你想从类文件中获取 jvm 签名(或者你的意思是从源文件中获取?

从类以下可能是一个解决方案

public class GetSignature {
    // the method for which you want to retrieve the signature
    Map<String, Map<?, ? super Integer>> someMethod() {
        return null;
    }
    public static void main(String[] args) throws Exception {
        // make the private method in class Method accessible
        Method methodGenericSignature = Method.class.getDeclaredMethod(
                "getGenericSignature",
                (Class<?>[]) null
        );
        methodGenericSignature.setAccessible(true);
        // get the signature from the method
        Method method = GetSignature.class.getDeclaredMethod("someMethod",
                (Class<?>[]) null
        );
        String returnValue = (String) methodGenericSignature.invoke(method,
                (Object[]) null
        );
        System.out.println("signature: " + returnValue);
    }
}

输出

signature: ()Ljava/util/Map<Ljava/lang/String;Ljava/util/Map<*-Ljava/lang/Integer;>;>;

编辑 演示如何使用 asm 获取签名的小截图。

public class GetSignatureDemo {
    public static void main(String[] args) throws IOException {
        InputStream is = new FileInputStream("/tmp/GetSignature.class");
        ClassReader classReader = new ClassReader(is);
        classReader.accept(getClassVisitor(), 0);
    }
    private static ClassVisitor getClassVisitor() {
        return new ClassVisitor(Opcodes.ASM4) {
            @Override
            public MethodVisitor visitMethod(int access, String name,
                    String descriptor, String signature, String[] exceptions) {
                System.out.printf(
                        "method: %s  descriptor: %s  signature: %s%n",
                        name,
                        descriptor,
                        signature
                );
                return super.visitMethod(access, name, descriptor, signature, exceptions);
            }
        };
    }
}

示例输出

method: someMethod  descriptor: ()Ljava/util/Map;  signature: ()Ljava/util/Map<Ljava/lang/String;Ljava/util/Map<*-Ljava/lang/Integer;>;>;

我的最终解决方案。我正在发布以供参考。因为当method.setAccessible(true)不可用时它可能很有用。

这或多或少是我最终使用的。因为我需要将泛型返回类型转换为类签名。而且我对类型变量没有用处。

它适用于没有类型参数的方法。对于具有泛型类型参数的方法,它会尝试替换参数。

处理此方法的泛型返回类型:<P> List<P> someMethod() 结果"Ljava/util/List<Ljava/lang/Object;>;"

static String toGenericSignature(final Type type)
{
    StringBuilder sb = new StringBuilder();
    toGenericSignature(sb, type);
    return sb.toString();
}
static void toGenericSignature(StringBuilder sb, final Type type)
{
    if (type instanceof GenericArrayType)
    {
        sb.append("[");
        toGenericSignature(sb, ((GenericArrayType) type).getGenericComponentType());
    }
    else if (type instanceof ParameterizedType)
    {
        ParameterizedType pt = (ParameterizedType) type;
        sb.append('L');
        sb.append(((Class) pt.getRawType()).getName().replace('.', '/'));
        sb.append('<');
        for (Type p : pt.getActualTypeArguments())
        {
            toGenericSignature(sb, p);
        }
        sb.append(">;");
    }
    else if (type instanceof Class)
    {
        Class clazz = (Class) type;
        if (!clazz.isPrimitive() && !clazz.isArray())
        {
            sb.append('L');
            sb.append(clazz.getName().replace('.', '/'));
            sb.append(';');
        }
        else
        {
            sb.append(clazz.getName().replace('.', '/'));
        }
    }
    else if (type instanceof WildcardType)
    {
        WildcardType wc = (WildcardType) type;
        Type[] lowerBounds = wc.getLowerBounds();
        Type[] upperBounds = wc.getUpperBounds();
        boolean hasLower = lowerBounds != null && lowerBounds.length > 0;
        boolean hasUpper = upperBounds != null && upperBounds.length > 0;
        if (hasUpper && hasLower && Object.class.equals(lowerBounds[0]) && Object.class.equals(upperBounds[0]))
        {
            sb.append('*');
        }
        else if (hasLower)
        {
            sb.append("-");
            for (Type b : lowerBounds)
            {
                toGenericSignature(sb, b);
            }
        }
        else if (hasUpper)
        {
            if (upperBounds.length == 1 && Object.class.equals(upperBounds[0]))
            {
                sb.append("*");
            }
            else
            {
                sb.append("+");
                for (Type b : upperBounds)
                {
                    toGenericSignature(sb, b);
                }
            }
        }
        else
        {
            sb.append('*');
        }
    }
    else if (type instanceof TypeVariable)
    {
        //sb.append("T");
        //sb.append(((TypeVariable) type).getName());
        //sb.append(";");
        // work around: replaces the type variable with it's first bound.
        toGenericSignature(sb, ((TypeVariable) type).getBounds()[0]);
    }
    else
    {
        throw new IllegalArgumentException("Invalid type: " + type);
    }
}

最新更新