Unprotected privates?
I was doing some work on an SDK recently which required that I access a protected member of a class. I didn’t want to have to override the class just to get access to it so I did some digging and discovered some interesting things you can do with Java and the JVM to access not only protected but also private class members.
It made me think a little about how we design classes and assumptions we make about immutability and access to internals. Here I have provided 2 examples of immutable classes. The first is obviosuly not properly immutable simply due to the fact that it’s field is declared protected and not private.
If you need to know what immutability is have a look at Wikipedia
The goal of the exercise is to execute the following code:
Immutable myImmutable =
new Immutable(new String[]{"Hello", "World"});
//Do some cunning stuff here...
System.out.println(myImmutable );
myImmutable.getValue()[0] = "Goodbye";
System.out.println(myImmutable );
Now both classes are designed to be immutable so that this code should always print:
Hello World Hello World
But if we put in the right cunning stuff we could get:
Hello World Goodbye World
Accessing Protected Fields
The “not-so-immutable” class looks like this:
public class Immutable
{
protected String[] value;
public Immutable(String[] value)
{
this.value = value;
}
public String[] getValue()
{
//To maintain immutability we copy the array
String[] return_value = new String[value.length];
System.arraycopy(
value, 0,
return_value, 0,
value.length);
return return_value;
}
@Override
public String toString()
{
StringBuffer buffer = new StringBuffer();
for(int i = 0; i < value.length; i++)
{
buffer.append(value[i] + " ");
}
return buffer.toString();
}
}
A pretty easy to understand class, but wait till you see how easily we can circumvent the immutability.
Immutable myImmutable =
new Immutable(new String[]{"Hello", "World"}){
@Override
public String[] getValue()
{
return value;
}
};
System.out.println(myImmutable );
myImmutable.getValue()[0] = "Goodbye";
System.out.println(myImmutable );
Accessing Private Fields
Now you might say that the class is not really immutable because you expose the fields to sub-classes. But what if we define exactly the same class but make the field value private.
It’s still possible to expose the field albeit a little more complicated.
Immutable myImmutable =
new Immutable(new String[]{"Hello", "World"}){
@Override
public String[] getValue()
{
try
{
Class clazz = Immutable.class;
Field private_field =
clazz.getDeclaredField("value");
private_field.setAccessible(true);
return ((String[]) private_field.get(this));
}
catch (Exception e)
{
e.printStackTrace();
return null;
}
}
};
System.out.println(myImmutable );
myImmutable.getValue()[0] = "Goodbye";
System.out.println(myImmutable );
Easy as that.