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 method 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 to this problem - but not very elegant in my opinion. I think of a more elegant way of having objectoriented access to hidden state. My approach works as following:
Each visible state in Java is accessible 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 readable outside of Name
. But it would be if Name
was implementing NameReadable
- or if Name
would be convertable to NameReadable
.
Though, how could this be done? The naive way would be simply to 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 needs a bit more code (less if static imports are used). Now that we can read hidden state - how about writing hidden state. 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 the XRayInterface-Method is designed in a way that each method defined in the target interface could be mapped in the intuitive way to the methods/fields of the underlying object.
XRayInterface yet also allows to do more than getting/setting hidden fields:
- Access to private methods
- Access to private construktors, 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 eplicitly to a field or to a property method)