4

i'm using ASM to build class file base on another Interface , i could use java reflection to get methods of the Interface , but i need to get the signature of it . enter image description here

i found it's a private field , and there's no direct method to get it . is there any way out ? i use jdk7 . tks advance .

Adams.H
  • 1,603
  • 4
  • 20
  • 29
  • https://docs.oracle.com/javase/tutorial/reflect/member/methodType.html ? –  Jul 13 '17 at 05:49
  • see also https://stackoverflow.com/questions/9081212/how-to-get-parameter-names-with-java-reflection –  Jul 13 '17 at 05:50

3 Answers3

6

If all you want is the signature field of java.lang.reflect.Method, that's simple:

java.lang.reflect.Field f = java.lang.reflect.Method.class.getDeclaredField("signature");
f.setAccessible(true);
String sigature = f.get(yourMethod);

Where yourMethod is a Method object.

Keep in mind: The signature field simply denotes the signature of a generic method, so attempting to get the signature of a non-generic method will simply return null.

For example, let's assess the following method:

public static <T> void test(Class<T> c){}

This will return a signature of <T:Ljava/lang/Object;>(Ljava/lang/Class<TT;>;)V

This shows that the method declares one generic type that must at least inherit java.lang.Object and takes a class of that type as a parameter.

But, say we were to remove the generic from the method:

public static void test(Class c){}

This would result in the signature field being null.

Edit:

If you want to get the signature of any method (generic or not), there are a couple of ways to do it, but I find the following to be the most concise (although possibly clunky):

public static String getSignature(Method m){
    String sig;
    try {
        Field gSig = Method.class.getDeclaredField("signature");
        gSig.setAccessible(true);
        sig = (String) gSig.get(m);
        if(sig!=null) return sig;
    } catch (IllegalAccessException | NoSuchFieldException e) { 
        e.printStackTrace();
    }

    StringBuilder sb = new StringBuilder("(");
    for(Class<?> c : m.getParameterTypes()) 
        sb.append((sig=Array.newInstance(c, 0).toString())
            .substring(1, sig.indexOf('@')));
    return sb.append(')')
        .append(
            m.getReturnType()==void.class?"V":
            (sig=Array.newInstance(m.getReturnType(), 0).toString()).substring(1, sig.indexOf('@'))
        )
        .toString();
}
  • This is exactly the way i wannted , before your method , i concatenate the strings myself . that's painful and ugly codes . tks a lot! – Adams.H Jul 16 '17 at 12:05
-1

Here is a map from letter to type signataure.

Type Signature -> Java Type
Z -> boolean
B -> byte C -> char
S -> short
I -> int
J -> long
F -> float
D -> double
Lfully-qualified-class; -> fully-qualified-class
[ type -> type[]
(arg-types)ret-type -> method type

// Holds a mapping from Java type names to native type codes.
private static final Map<Class<?>, String> PRIMITIVE_TO_SIGNATURE;
static {
    PRIMITIVE_TO_SIGNATURE = new HashMap<Class<?>, String>(9);
    PRIMITIVE_TO_SIGNATURE.put(byte.class, "B");
    PRIMITIVE_TO_SIGNATURE.put(char.class, "C");
    PRIMITIVE_TO_SIGNATURE.put(short.class, "S");
    PRIMITIVE_TO_SIGNATURE.put(int.class, "I");
    PRIMITIVE_TO_SIGNATURE.put(long.class, "J");
    PRIMITIVE_TO_SIGNATURE.put(float.class, "F");
    PRIMITIVE_TO_SIGNATURE.put(double.class, "D");
    PRIMITIVE_TO_SIGNATURE.put(void.class, "V");
    PRIMITIVE_TO_SIGNATURE.put(boolean.class, "Z");
}

/**
 * Returns the internal name of {@code clazz} (also known as the descriptor).
 */
public static String getSignature(Class<?> clazz) {
    String primitiveSignature = PRIMITIVE_TO_SIGNATURE.get(clazz);
    if (primitiveSignature != null) {
        return primitiveSignature;
    } else if (clazz.isArray()) {
        return "[" + getSignature(clazz.getComponentType());
    } else {
        return "L" + clazz.getName().replace('.', '/') + ";";
    }
}

then, we get signature from java.lang.reflect.Method like this:

public static String getSignature(Method method) {
    StringBuilder result = new StringBuilder();
    result.append('(');
    Class<?>[] parameterTypes = method.getParameterTypes();
    for (Class<?> parameterType : parameterTypes) {
        result.append(getSignature(parameterType));
    }
    result.append(')');
    result.append(getSignature(method.getReturnType()));
    return result.toString();
}

we print java.lang.Object class all methods' signature:

Method[] methods = Object.class.getDeclaredMethods();
for(Method method: methods) {
    String signature = getSignature(method);
    System.out.println(method.getName() + "  " + signature);
}

and it prints

finalize ()V
wait (J)V
wait (JI)V
wait ()V
equals (Ljava/lang/Object;)Z
toString ()Ljava/lang/String;
hashCode ()I
getClass ()Ljava/lang/Class;
clone ()Ljava/lang/Object;
notify ()V
notifyAll ()V
registerNativces ()V

Above are some Java codes. we can also get signature from Java builtin command javap, -s for signature info. For example, we can get java.lang.Object class with this command javap -s java.lang.Object:

Compiled from "Object.java"
public class java.lang.Object {
public java.lang.Object();
descriptor: ()V

public final native java.lang.Class<?> getClass();
descriptor: ()Ljava/lang/Class;

public native int hashCode();
descriptor: ()I

public boolean equals(java.lang.Object);
descriptor: (Ljava/lang/Object;)Z

protected native java.lang.Object clone() throws java.lang.CloneNotSupportedException;
descriptor: ()Ljava/lang/Object;

public java.lang.String toString();
descriptor: ()Ljava/lang/String;

public final native void notify();
descriptor: ()V

public final native void notifyAll();
descriptor: ()V

public final void wait() throws java.lang.InterruptedException;
descriptor: ()V

public final native void wait(long) throws java.lang.InterruptedException;
descriptor: (J)V

public final void wait(long, int) throws java.lang.InterruptedException;
descriptor: (JI)V

protected void finalize() throws java.lang.Throwable;
descriptor: ()V

static {};
descriptor: ()V
}

KAlO2
  • 838
  • 11
  • 13
-2

Replacing '.' with '/' makes Gabriel Tofvesson's answer complete:

... .toString().replace('.', '/');

Otherwise, the signature returned would be something like ()Ljava.lang.Object;. However, in signatures, the package names must be separated using slashes instead of periods: ()Ljava/lang/Object;.

Martin Rosenau
  • 17,897
  • 3
  • 19
  • 38
Jan
  • 23
  • 2