I want to design a class to demonstrate immutability, incrementally.
Following is a simple class
public class ImmutableWithoutMutator {
private final String firstName;
private final String lastName;
private final int age;
public ImmutableWithoutMutator(final String firstName, final String lastName, final int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
@Override
public String toString() {
return String.format(
"ImmutableWithoutMutator [age=%s, firstName=%s, lastName=%s]",
age, firstName, lastName);
}
}
Now I can still breach in using reflection, by using the following code.
import java.lang.reflect.Field;
public class BreakImmutableUsingReflection {
public static void main(String[] args) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
ImmutableWithoutMutator immMutator = new ImmutableWithoutMutator(
"firstName", "lastName", 2400);
System.out.println(immMutator);
// now lets try changing value using reflection
Field f = ImmutableWithoutMutator.class.getDeclaredField("age");
f.setAccessible(true);
f.set(immMutator, 2000);
System.out.println(immMutator);
}
}
My question is, I have not modified the Modifiers for the fields using reflection API. Then how the code is still able to mutate final fields?
This is expected behaviour - it's just something you shouldn't do.
From the documentation for Field.set
:
If the underlying field is final, the method throws an IllegalAccessException unless setAccessible(true) has succeeded for this Field object and the field is non-static. Setting a final field in this way is meaningful only during deserialization or reconstruction of instances of classes with blank final fields, before they are made available for access by other parts of a program. Use in any other context may have unpredictable effects, including cases in which other parts of a program continue to use the original value of this field.
Now if you then want to argue this breaches all kinds of security, you need to ask yourself why you've got code you don't trust to be sane running with sufficient permission to call field.setAccessible(true)
. Basically, protection is given by the security manager - but if you're running without a security manager which stops you from doing this, you can shoot yourself in the foot.
You can protect from setAccessible using the SecurityManager. You can either specify it on the command line or create it dynamically, like they do it here.
But even if you protect from reflection your code would still be not protected against bytecode modification libraries like ASM.
So you shouldn't rely on your fields being final.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With