Search code examples
javadecompiling

How to reintegrate anonymous classes into Java code produced by the ECD decompiler?


I have a jar for school that is supposed to be decompiled, modifed, and reevaluated. I decompiled all of the class files using the ECD plugin for Eclipse, but I think I have a few anonymous classes that were extracted and need to be merged back into another class. I have a class P, and then five more classes named P$1, P$2, ..., P$5.

Here's the problem parts of P:

public class P {
private ArrayList<Family> group;
private int marker;
private Integer primaryElement;
Comparator<Family> c;

public P(ArrayList<Family> g, Integer i, Comparator<Family> c) {
    this.marker = -1;
    this.group = new ArrayList(g);
    this.primaryElement = i;
    this.c = c;
}

/* Some unrelated methods */

public String printHeader() {
  return this.print(new 1(this));
}

public String printRow(Integer i) {
  return this.print(new 2(this, i));
}

public String printPad() {
  return this.print(new 3(this));
}

public Object printCost() {
  return this.print(new 4(this));
}

public String printLine() {
  return this.print(new 5(this));
}

Here is P$1. The others are very similar.

final class P$1 implements PrintCommand {
  P$1(P arg0) {
    this.this$0 = arg0;
  }

  public String print(Family f) {
    return String.format("%3d", new Object[]{Integer.valueOf(f.getId())});
  }
}

In case you're wondering, PrintCommand is a super simple interface:

public interface PrintCommand {
  String print(Family arg0);
}

How can I get P$1 merged back into P? Also, what does this.this$0 mean in P$1?


Solution

  • In an anonymous class you can reference the this from the enclosing class with P.this. To do that, the java compiler will create a constructor, which will set a field named this$0 to the reference passed to the constructor.

    The original code probably looked like this:

    public String printHeader() { 
        return this.print(new PrintCommand() {
            public String print(Family f) {
                return String.format(%3d", f.getId());
            }
        );
    }
    

    There are other things the compiler does, for example adding accessor methods for private methods/fields from the enclosing class that are accessed in the inner class. Or passing the value of (effectively) final variables used in the inner class to the constructor.

    From the perspective of the Java Runtime, there is no anonymous inner class, only named classes.