Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
204 views
in Technique[技术] by (71.8m points)

java - When using javassist anonymous inner class how to access instance variables of outer class?

Below is the anonymous inner class definition:

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 
", staticNum, instanceNum);
            }
        };
    }
}

Below is the running example:

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.access$100(com.demo.OuterClass.this));" +  // [source error] missing member name
                "}");
        nestedClasses[0].toClass();
        outerClass.toClass();
        new OuterClass().redefineMe().run();
    }
}

I want to redefine the body of the run method, but I cannot access the instance variables of the outer class in the body

question from:https://stackoverflow.com/questions/65896989/when-using-javassist-anonymous-inner-class-how-to-access-instance-variables-of-o

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

According to the manual, Javassist does not support inner class generation, but claims to support to read and modify them:

  • 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.

I guess the compiler support ends where you want to do use inner-class-specific syntactic sugar in source code compiled by Javassist, though. A simple workaround would be to use "sacred knowledge" as follows:

$ javap classes/com/demo/OuterClass$1.class
Compiled from "OuterClass.java"
class com.demo.OuterClass$1 implements java.lang.Runnable {
  final com.demo.OuterClass this$0;
  com.demo.OuterClass$1(com.demo.OuterClass);
  public void run();
}

Oh look, this$0! Let us try that:

run.setBody("{" +
  "System.out.println("staticNum: " + com.demo.OuterClass.staticNum);" +
  "System.out.println("instanceNum: " + this$0.instanceNum);" +
  "}");

Now we get the console output:

staticNum: 1
instanceNum: 2

I do not know, how stable and reliable this workaround is across Java releases and compiler flavours.

P.S.: If you change the anonymous inner class to a lambda, the class file looks completely different and you are lost again. I found nothing in the Javassist repository even mentioning lambdas, only a few open issues reporting problems.


Update: I created Javassist issue #358 in order to track this.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...