from How to get Method Parameter names in Java 8 using reflection?
I know using javac -parameters
argument can keep the parameter names in *.class file.
but it is invalid in lambda expression?
example:
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
public class MyTest {
public static void main(String[] args) {
for(Method m : Test.class.getDeclaredMethods()) {
System.out.println(m.getName());
for(Parameter p : m.getParameters()) {
System.out.println(" => " + p.getName());
}
}
}
}
interface MyInterface {
Object doSomething(int a, int b);
}
class Test {
private void bar(MyInterface iface) {
}
public void foo() {
bar((x, y) -> null);
}
}
When I do
javac -parameters MyTest.java
java MyTest
It print
bar
=> iface
foo
lambda$foo$0
=> arg0
=> arg1
I try to do javap -c -p -verbose Test
:
{
Test();
descriptor: ()V
flags:
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 21: 0
private void bar(MyInterface);
descriptor: (LMyInterface;)V
flags: ACC_PRIVATE
Code:
stack=0, locals=2, args_size=2
0: return
LineNumberTable:
line 24: 0
MethodParameters:
Name Flags
iface
public void foo();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokedynamic #2, 0 // InvokeDynamic #0:doSomething:()LMyInterface;
6: invokespecial #3 // Method bar:(LMyInterface;)V
9: return
LineNumberTable:
line 27: 0
line 28: 9
private static java.lang.Object lambda$foo$0(int, int);
descriptor: (II)Ljava/lang/Object;
flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=1, locals=2, args_size=2
0: aconst_null
1: areturn
LineNumberTable:
line 27: 0
}
I can find parameter name iface
, but can not find x
or y
This does not appear to be a problem with lambda expressions themselves:
interface MyInterface {
void doSomething(int a, int b);
}
class Test {
private void bar(MyInterface iface) {
iface.doSomething(0, 0);
}
public void foo() {
bar((x, y) -> System.out.println(x));
}
}
Uses a single lambda expression to keep it simple. After compiling this with the -parameters
option, we can use javap -c -p -verbose Test
to find out more:
private static void lambda$foo$0(int, int);
descriptor: (II)V
flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=2, locals=2, args_size=2
0: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
3: iload_0
4: invokevirtual #6 // Method java/io/PrintStream.println:(I)V
7: return
LineNumberTable:
line 15: 0
MethodParameters:
Name Flags
x synthetic
y synthetic
The parameter names (x
and y
) are there! Iterating over the methods like this
for(Method m : Test.class.getDeclaredMethods()) {
System.out.println(m.getName());
for(Parameter p : m.getParameters()) {
System.out.println(" => " + p.getName());
}
}
displays the parameter names correctly:
lambda$foo$0
=> x
=> y
Instead of being a problem with the lamdbas, it is actually just difficult to determine the correct method. If you try to get the parameter names using getDeclaredMethods()
on the instance of the interface passed to that method (as @Didier L suggested in a comment), you will run into problems. For example, using
iface.getClass().getDeclaredMethods()
in bar()
will not work as you might expect. If you retrieve the name of the class, e.g. like iface.getClass().getName()
, you will see something like this:
Test$$Lambda$1/834600351
This is a dynamically created class, you could argue about whether it exists at all. As it is not generated by the compiler, it does not expose any information about local variables or their names because the method implemented by the interface simply is not the same as your lambda. This is an important difference.
This "virtual class" provides a method, e.g. doSomething(int, int)
, to implement the given interface (MyInterface
), but this exposed method is not the same as the method you create be defining a lambda (lambda$foo$0
).
Therefore, the method doSomething
of the generated "virtual class" does not carry the parameter information. The dynamically created class "hides" your implementation.
Your second example does not suffer from this problem:
map("bibi", new A() {
@Override
public JSONObject exec(String u, String s, String b) {
return null;
}
});
You explicitely define a class implementing the interface A
and you explicitely provide a method exec
, therefore all the information is present.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With