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? Yet, in my former posting about Repairing Legacy Code I mentioned the challenge of accessing hidden state (private fields) before and after method invocations.
Having access to private fields or methods could be helpful in several scenarios:
- for explicitly visualizing the contents of an object
- for replacing this hidden state of the object with a mock object (injecting a mocked dependency)
- for checking the contents of a hidden field that is used in another algorithm
- for testing methods that are complex but should not be part of the public/protected/package private API
Yet, 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. 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 inner state is not writable (after construction), but readable. So the implicit interface of this class would be:
interface NameReadable {
String getFirstName();
String getLastName();
}
Casting Name
to NameReadable
would not work. But Name
needs not to 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 (less if static imports are used). 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");
Strictly speaking, 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 yet 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)