to Overview
Mai 21, 2016

Accessing Private Fields and Methods with XRayInterface

This posting is about accessing private fields and methods, by adopting a foreign interface to a given Java class (not already implementing the given interface). Why? In my former posting about Repairing legacy code I mentioned the challenge of accessing hidden state (private fields) before and after method invocations.


Access to private fields or methods could be helpful in several scenarios:

  • to explicitly visualize the contents of an object
  • to replace this hidden state of the object with a mock object (injecting a mocked dependency)
  • to check the contents of a hidden field that is used in another algorithm
  • to test methods that are complex but should not be part of the public/protected/package private API

Java Reflection would be suitable for this problem - but not very elegant in my opinion. I think of a more elegant way of having object-oriented access to hidden state. My approach works as follows:

Each visible state in Java is typically accessed through an interface. Let us consider a simple Java-Bean Name:

public class Name {

    private String firstName;
    private String lastName;
    
    public Name(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String getFirstName() {
        return firstName;
    }
    
    public String getLastName() {
        return lastName;
    }
}

The internal state is readable but not writable after construction. So the implicit interface of this class would be:

interface NameReadable {
    String getFirstName();
    String getLastName();
}

Casting Name to NameReadable would not work. But Name need not be changed if implementing NameReadable. Now consider a class Name that is only partially readable:

public class Name {

    private String firstName;
    private String lastName;

    public Name(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String getFirstName() {
        return firstName;
    }
}

The attribute lastName is not accessible outside of Name. But it would be if Name implemented NameReadable - or if Name were convertible to NameReadable.

But how could this be done? The naive way would be to simply cast to NameReadable:

NameReadable read = (NameReadable) new Name("Foo","Bar"); //ClassCastException
System.out.println(read.getLastName());

This way would not work, but it is the approach we want to follow with XRayInterface. XRayInterface allows you to adopt an interface, in a very similar way as casting:

NameReadable read = XRayInterface.xray(new Name("Foo", "Bar"))
    .to(NameReadable.class);
System.out.println(read.getLastName()); // => Bar

This requires a bit more code, although it’s less when using static imports. Now that we can read hidden state, how about writing to it? It works quite similar:

interface NameWritable {
    void setFirstName(String firstName);
    void setLastName(String lastName);
}

NameWritable write = XRayInterface.xray(new Name("Foo", "Bar"))
    .to(NameWritable.class);
write.setLastName("FooBar");

The XRayInterface method is designed so that each method defined in the target interface can be intuitively mapped to the methods or fields of the underlying object.

XRayInterface also allows to do more than getting/setting hidden fields:

  • Access to private methods
  • Access to private constructors, static methods, static fields
  • Access to private fields that are “hidden” by property methods (there are annotations that control the mapping, so an interface method could be mapped explicitly to a field or to a property method)