使用 javassist anonymous inner class 时如何访问 outer class 的实例变量?
When using javassist anonymous inner class how to access instance variables of outer class?
下面是匿名内class定义:
package com.demo;
public class OuterClass {
private static int staticNum = 1;
private int instanceNum = 2;
public Runnable redefineMe() {
return new Runnable() {
@Override
public void run() {
System.out.printf("staticNum %d, instanceNum %d \n", staticNum, instanceNum);
}
};
}
}
下面是运行宁的例子:
package com.demo;
import javassist.*;
public class Test {
public static void main(String[] args) throws NotFoundException, CannotCompileException {
ClassPool pool = ClassPool.getDefault();
CtClass outerClass = pool.get("com.demo.OuterClass");
CtClass[] nestedClasses = outerClass.getNestedClasses();
CtMethod run = nestedClasses[0].getDeclaredMethod("run");
run.setBody("{" +
"System.out.println(\"staticNum: \" + com.demo.OuterClass.staticNum);" + // print: staticNum: 1
// I tried to use the following code to access instance variables, but a compilation error occurred
// "System.out.println(\"staticNum: \" + instanceNum);" + // [source error] no such field: instanceNum
// "System.out.println(\"staticNum: \" + com.demo.OuterClass.this.instanceNum);" + // [source error] missing member name
// "System.out.println(\"staticNum: \" + com.demo.OuterClass.access0(com.demo.OuterClass.this));" + // [source error] missing member name
"}");
nestedClasses[0].toClass();
outerClass.toClass();
new OuterClass().redefineMe().run();
}
}
我想重新定义运行方法的主体,但是在主体
中无法访问外部class的实例变量
根据manual,Javassist不支持内class生成,但声称支持读取和修改它们:
- Inner classes or anonymous classes are not supported. Note that this is a limitation of the compiler only. It cannot compile source code including an anonymous-class declaration. Javassist can read and modify a class file of inner/anonymous class.
我想编译器支持在您想要在 Javassist 编译的源代码中使用内部 class 特定语法糖的地方结束。一个简单的解决方法是使用“神圣知识”,如下所示:
$ javap classes/com/demo/OuterClass$1.class
Compiled from "OuterClass.java"
class com.demo.OuterClass implements java.lang.Runnable {
final com.demo.OuterClass this[=10=];
com.demo.OuterClass(com.demo.OuterClass);
public void run();
}
哦,看,this[=13=]
!让我们试试看:
run.setBody("{" +
"System.out.println(\"staticNum: \" + com.demo.OuterClass.staticNum);" +
"System.out.println(\"instanceNum: \" + this[=11=].instanceNum);" +
"}");
现在我们得到控制台输出:
staticNum: 1
instanceNum: 2
我不知道,此解决方法在 Java 版本和编译器风格中的稳定性和可靠性如何。
P.S.: 如果将匿名内部 class 更改为 lambda,class 文件看起来完全不同,您是又输了我在 Javassist 存储库中没有发现任何提及 lambda 的内容,只有几个未解决的问题报告问题。
更新: 我创建了 Javassist issue #358 以跟踪它。
下面是匿名内class定义:
package com.demo;
public class OuterClass {
private static int staticNum = 1;
private int instanceNum = 2;
public Runnable redefineMe() {
return new Runnable() {
@Override
public void run() {
System.out.printf("staticNum %d, instanceNum %d \n", staticNum, instanceNum);
}
};
}
}
下面是运行宁的例子:
package com.demo;
import javassist.*;
public class Test {
public static void main(String[] args) throws NotFoundException, CannotCompileException {
ClassPool pool = ClassPool.getDefault();
CtClass outerClass = pool.get("com.demo.OuterClass");
CtClass[] nestedClasses = outerClass.getNestedClasses();
CtMethod run = nestedClasses[0].getDeclaredMethod("run");
run.setBody("{" +
"System.out.println(\"staticNum: \" + com.demo.OuterClass.staticNum);" + // print: staticNum: 1
// I tried to use the following code to access instance variables, but a compilation error occurred
// "System.out.println(\"staticNum: \" + instanceNum);" + // [source error] no such field: instanceNum
// "System.out.println(\"staticNum: \" + com.demo.OuterClass.this.instanceNum);" + // [source error] missing member name
// "System.out.println(\"staticNum: \" + com.demo.OuterClass.access0(com.demo.OuterClass.this));" + // [source error] missing member name
"}");
nestedClasses[0].toClass();
outerClass.toClass();
new OuterClass().redefineMe().run();
}
}
我想重新定义运行方法的主体,但是在主体
中无法访问外部class的实例变量根据manual,Javassist不支持内class生成,但声称支持读取和修改它们:
- Inner classes or anonymous classes are not supported. Note that this is a limitation of the compiler only. It cannot compile source code including an anonymous-class declaration. Javassist can read and modify a class file of inner/anonymous class.
我想编译器支持在您想要在 Javassist 编译的源代码中使用内部 class 特定语法糖的地方结束。一个简单的解决方法是使用“神圣知识”,如下所示:
$ javap classes/com/demo/OuterClass$1.class
Compiled from "OuterClass.java"
class com.demo.OuterClass implements java.lang.Runnable {
final com.demo.OuterClass this[=10=];
com.demo.OuterClass(com.demo.OuterClass);
public void run();
}
哦,看,this[=13=]
!让我们试试看:
run.setBody("{" +
"System.out.println(\"staticNum: \" + com.demo.OuterClass.staticNum);" +
"System.out.println(\"instanceNum: \" + this[=11=].instanceNum);" +
"}");
现在我们得到控制台输出:
staticNum: 1
instanceNum: 2
我不知道,此解决方法在 Java 版本和编译器风格中的稳定性和可靠性如何。
P.S.: 如果将匿名内部 class 更改为 lambda,class 文件看起来完全不同,您是又输了我在 Javassist 存储库中没有发现任何提及 lambda 的内容,只有几个未解决的问题报告问题。
更新: 我创建了 Javassist issue #358 以跟踪它。