javaparser
javaparser copied to clipboard
Solve method declarations in anonymous inner classes
When solving for method declarations in anonymous inner classes, the result is not what I want. The result is:
Main.main(java.lang.String[])
java.io.PrintStream.println(java.lang.String)
"Main.main(java.lang.String[])" -> "java.io.PrintStream.println(java.lang.String)"
java.lang.Runnable.run()
"Main.main(java.lang.String[])" -> "java.lang.Runnable.run()"
Main.Anonymous-9904eed3-5b31-434d-9b07-63e5b069a3a2.run()
java.io.PrintStream.println(java.lang.String)
"Main.Anonymous-9904eed3-5b31-434d-9b07-63e5b069a3a2.run()" -> "java.io.PrintStream.println(java.lang.String)"
Why have such results as Anonymous-9904eed3-5b31-434d-9b07-63e5b069a3a2 when solving the method declaration "run"?However, when solving the methodcallexpr "runnable.run()", the result is java.lang.Runnable.run().
This is a testcode:
package testcode;
import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.symbolsolver.JavaSymbolSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver;
import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver;
import java.util.List;
public class Main {
public static void main(String[] args) {
String sourcecode= "public class Main {\n" +
"\n" +
" public static void main(String[] args) {\n" +
" Runnable runnable = new Runnable() {\n" +
" @Override\n" +
" public void run() {\n" +
" System.out.println(\"This is an anonymous inner class.\");\n" +
" }\n" +
" };\n" +
"\n" +
" runnable.run();\n" +
"\n" +
" }\n" +
"\n" +
"}";
CombinedTypeSolver myTypeSolver = new CombinedTypeSolver();
myTypeSolver.add(new ReflectionTypeSolver());
JavaSymbolSolver symbolSolver = new JavaSymbolSolver(myTypeSolver);
StaticJavaParser
.getConfiguration()
.setSymbolResolver(symbolSolver);
CompilationUnit cu = StaticJavaParser.parse(sourcecode);
List<MethodDeclaration> methods = cu.findAll(MethodDeclaration.class);
for (MethodDeclaration method : methods) {
String fullCallerMethodName = method.resolve().getQualifiedSignature();
System.out.println(fullCallerMethodName);
List<MethodCallExpr> methodCallees = method.findAll(MethodCallExpr.class);
for (MethodCallExpr callee : methodCallees) {
String calleeMethodName = callee.resolve().getQualifiedSignature();
System.out.println(calleeMethodName);
System.out.println("\"" + fullCallerMethodName + "\"" + " -> " + "\"" + calleeMethodName + "\"");
}
}
}
}
It is by convention part of the name of the anonymous class.
OK, I got it. But themethod run() is overridden by the anonymous class, why the calling relationship is "Main.main(java.lang.String[])" -> "java.lang.Runnable.run()" and not "Main.main(java.lang.String[])" -> "Main.Anonymous-9904eed3-5b31-434d-9b07-63e5b069a3a2.run()"
And I don't know if it's a bug: for the following test case, I instantiated the Animal interface and enabled the anonymous class, but didn't call the overridden method which defined in the anonymous class. The analysis results of the javaparser include Main.main(java.lang.String[])" -> "java.io.PrintStream.println(java.lang.String)" and "Main.main(java.lang.String[])" -> "Person.speak()". I think this is a bit abnormal. This is the result:
Main.main(java.lang.String[])
java.io.PrintStream.println(java.lang.String)
"Main.main(java.lang.String[])" -> "java.io.PrintStream.println(java.lang.String)"
Person.speak()
"Main.main(java.lang.String[])" -> "Person.speak()"
Animal.eat()
"Main.main(java.lang.String[])" -> "Animal.eat()"
Main.Anonymous-36bd0a62-0aab-453c-b378-0b040f5bd08b.makeSound()
java.io.PrintStream.println(java.lang.String)
"Main.Anonymous-36bd0a62-0aab-453c-b378-0b040f5bd08b.makeSound()" -> "java.io.PrintStream.println(java.lang.String)"
Person.speak()
"Main.Anonymous-36bd0a62-0aab-453c-b378-0b040f5bd08b.makeSound()" -> "Person.speak()"
Animal.makeSound()
Animal.eat()
java.io.PrintStream.println(java.lang.String)
"Animal.eat()" -> "java.io.PrintStream.println(java.lang.String)"
Person.speak()
java.io.PrintStream.println(java.lang.String)
"Person.speak()" -> "java.io.PrintStream.println(java.lang.String)"
public class MsgLogger {
public static void main(String[] args) {
Animal animal = new Animal() {
public void makeSound() {
System.out.println("The animal is making a sound.");
Person person = new Person();
person.speak();
}
};
animal.eat();
}
}
interface Animal {
void makeSound();
default void eat() {
System.out.println("The animal is eating.");
}
}
class Person {
public void speak() {
System.out.println("Hello, I'm a person.");
}
}
OK, I got it. But themethod run() is overridden by the anonymous class, why the calling relationship is "Main.main(java.lang.String[])" -> "java.lang.Runnable.run()" and not "Main.main(java.lang.String[])" -> "Main.Anonymous-9904eed3-5b31-434d-9b07-63e5b069a3a2.run()"
Because the "runnable" variable is typed Runnable. So when JP tries to resolve the "run" method, he looks at the type of the scope which is Runnable.
The analysis results of the javaparser include Main.main(java.lang.String[])" -> "java.io.PrintStream.println(java.lang.String)" and "Main.main(java.lang.String[])" -> "Person.speak()". I think this is a bit abnormal.
This result corresponds to what you have programmed. You are recursively searching for all method calls executed inside the "main" method.
The analysis results of the javaparser include Main.main(java.lang.String[])" -> "java.io.PrintStream.println(java.lang.String)" and "Main.main(java.lang.String[])" -> "Person.speak()". I think this is a bit abnormal.
This result corresponds to what you have programmed. You are recursively searching for all method calls executed inside the "main" method.
But I only find the “findAll” method in javaparser to solve my problem and I dont think its the fault of recursively searching. The anonymous class is defined in the method, and "findAll" will find all the method call expressions in the method declaration (including the method call expression in the anonymous class). Sorry, I'm not too familiar with javaparser. Is there any way javaparser can solve my problem? Thanks for your help.
OK, I got it. But themethod run() is overridden by the anonymous class, why the calling relationship is "Main.main(java.lang.String[])" -> "java.lang.Runnable.run()" and not "Main.main(java.lang.String[])" -> "Main.Anonymous-9904eed3-5b31-434d-9b07-63e5b069a3a2.run()"
Because the "runnable" variable is typed Runnable. So when JP tries to resolve the "run" method, he looks at the type of the scope which is Runnable.
But in another example, I find this type-based approach problematic. When solving the method call expression obj.print(), the result is Parent.print(). Although the type of obj is Parent, the method that is actually called here is Child.print(). I think such results are problematic.
class Parent {
void print() {
System.out.println("Parent");
}
}
class Child extends Parent {
@Override
void print() {
System.out.println("Child");
}
}
public class Main {
public static void main(String[] args) {
Parent obj = new Child();
obj.print();
}
}
Is there any way javaparser can solve my problem?
Yes there are certainly several ways to do this. The first that comes to mind would be to create your own visitor (inheriting from VoidVisitorAdapter) that would filter out the nodes you don't want to browse. You also have a method findAll which accepts a Predicate (which will allow you to apply a filter).
But in another example, I find this type-based approach problematic. When solving the method call expression obj.print(), the result is Parent.print(). Although the type of obj is Parent, the method that is actually called here is Child.print(). I think such results are problematic.
You get the same behavior in an IDE. In your example the type of the "obj" variable is Parent.
You get the same behavior in an IDE. In your example the type of the "obj" variable is Parent.
Yes, you're right. But I think this is problematic because the actual call is Child.print() instead of Parent.print(). This type-based approach can be problematic.