Hacking Immutable class using Java Reflection

On of the four pillars of OOP (Object Oriented Programming) is encapsulation, or data hiding. The other three, which won’t be discussed in this tutorial are inheritance, polymorphism and abstraction. Encapsulation, is a technique that we OOP developers use in order to make our code more manageable, and well protected from outside classes. By using encapsulation, we change the access modifiers to either private, protected, default(package) or public depending on what we are trying to do. Object Oriented developers typically refer to this technique as “Data Hiding”. Once the data is protected, the class can be treated as a “Black Box” and we are free to change it’s implementation so long as the interface used by the other classes.

For the purpose of this tutorial we will change the access level to private and provide public methods which would have getter methods to act on those private data fields. Please note, that we will not have setter methods available as the characteristic we are striving for in this class is its immutability. That is, its ability to remain constant and unmodifiable once it has been instantiated with its constructor.

Before we get started with the hack using Reflection, I would like to cover some of the basics of why Immutable classes are beneficial.

Benefits of Immutable Class

If we are following good programming practices in Java, we would strive to use immutable objects as often as possible. If changes need to be made to the object, we would be forced to recreate the object with modified data but the original would remain untouched. Here are some of the key benefits immutable classes:

  • Easy to Construct, Use and Test
  • Immutable objects are by default thread-safe, so no need to worry about synchronization
  • Since the state of the object will not change over its lifetime, there is no possibility of them being in an inconsistent state
  • Easier to use in multithreaded applications since they should be no contention between objects
  • Immutable classes are much more secure as their contents can not change once created
  • Immutable classes can be cached for performance gains using optimization techniques
  • They remain in a consistent state, even when exceptions occur

In the book, Effective Java, Joshua Bloch strongly encourages the use of immutable classes:

Classes should be immutable unless there’s a very good reason to make them mutable….If a class cannot be made immutable, limit its mutability as much as possible.

 

Java itself has many examples of immutable classes like String, Integer and Float. As a matter of fact, all wrapper classes are immutable classes as well. So the following classes in Java are all immutable:

Immutable Classes

Name
Byte
Short
Integer
Long
Float
Double
Character
Boolean
String

How to Make Classes Immutable

  • Define class as final
  • Make all fields private and final
  • Make the constructor private, and use public factory methods
  • Make sure all setter methods are made private or remove them altogether

Our Immutable Class

Our BookOfSecrets class has been defined with three instance variables: an immutable String, immutable Integer, and an Immutable Double all have been made private and final. Our class has been defined as final so as prevent it from being extended.

package com.avaldes.tutorials;

public final class BookOfSecrets {
  // all fields are private and final
  private final String immutableString;
  private final Integer immutableInteger; 
  private final Double immutableDouble; 

Our constructor for the class takes three arguments and have been made private so that new instances cannot be made with the new operator. The only way to create a new instance is via the factory method createInstance. You will also note that there are no setter methods so that once created this object may be not be modified.

  // ensure constructor is private
  private BookOfSecrets(String mySecret, Integer myInt, Double myDouble) {
    immutableString = mySecret;
    immutableInteger = myInt;
    immutableDouble = myDouble;
  }

  // public factory method
  public static BookOfSecrets createInstance(String mySecret, 
                                             Integer myInt, 
                                             Double myDouble) {

    return new BookOfSecrets(mySecret, myInt, myDouble);
  }

  public String getImmutableString() {
    return immutableString;
  }

  public Integer getImmutableInteger() {
    return immutableInteger;
  }

  public Double getImmutableDouble() {
    return immutableDouble;
  }
}

At this point, as per our definition for creating an immutable class, we have done all the things required in order to lock down this class and make it unmodifiable or immutable.

The Hacker Class

Using our BookOfSecretsHacker program and reflection we will demonstrate how we can modify all the private final fields in an immutable class.

Full Program Listing

package com.avaldes.tutorials;

import java.lang.reflect.Field;

public class BookOfSecretsHacker {

  public static void main(String[] args) throws SecurityException,
                                                NoSuchFieldException,
                                                IllegalArgumentException,
                                                IllegalAccessException {

    //BookOfSecrets bos = new BookOfSecrets();   // will not work!!!
    BookOfSecrets bos = BookOfSecrets.createInstance("The secret of our success is that we never ever give up", 123, 456.78);

    // Once created, this immutable class fields cannot be changed
    // Before Change
    System.out.println("---IMMUTABLE OBJECT BEFORE---");
    System.out.println(bos);

    //--There are no setter methods, so need another approach...  

    // Let's get the class and use reflection to modify private fields...
    Class<?> mc = bos.getClass();

    System.out.println("\nSetting the immutable field String...");
    Field fs = mc.getDeclaredField("immutableString");
    fs.setAccessible(true);
    fs.set(bos, "The hacker community may be small, but it's skills are driving the future.");

    System.out.println("Setting the immutable field Integer...");
    Field fi = mc.getDeclaredField("immutableInteger");
    fi.setAccessible(true);
    fi.set(bos, 321);

    System.out.println("Setting the immutable field Double...");
    Field fd = mc.getDeclaredField("immutableDouble");
    fd.setAccessible(true);
    fd.set(bos, 876.54);

    // After Change
    System.out.println("\n---IMMUTABLE OBJECT AFTER---");
    System.out.println(bos);
  }
}

Full Program Listing (BookOfSecrets.java)

package com.avaldes.tutorials;

public final class BookOfSecrets {
  // all fields are private and final
  private final String immutableString;       // The secret of our success is that we never, never give up
  private final Integer immutableInteger;
  private final Double immutableDouble; 

  // ensure constructor is private
  private BookOfSecrets(String mySecret, Integer myInt, Double myDouble) {
    immutableString = mySecret;
    immutableInteger = myInt;
    immutableDouble = myDouble;
  }

  // public factory method
  public static BookOfSecrets createInstance(String mySecret, Integer myInt, Double myDouble) {
    return new BookOfSecrets(mySecret, myInt, myDouble);
  }

  public String getImmutableString() {
    return immutableString;
  }

  public Integer getImmutableInteger() {
    return immutableInteger;
  }

  public Double getImmutableDouble() {
    return immutableDouble;
  }

  @Override
  public String toString() {
    return "BookOfSecrets [immutableString=" + immutableString
        + " {" + immutableString.hashCode() + "}\n"
        + ", immutableInteger=" + immutableInteger
        + " {" + immutableInteger.hashCode() + "}\n"
        + ", immutableDouble=" + immutableDouble
        + " {" + immutableDouble.hashCode() + "}"
        + "]";
  }
}

Output

---IMMUTABLE OBJECT BEFORE---
BookOfSecrets [immutableString=The secret of our success is that we never ever give up {1594064018}
, immutableInteger=123 {123}
, immutableDouble=456.78 {-1589960082}]

Setting the immutable field String...
Setting the immutable field Integer...
Setting the immutable field Double...

---IMMUTABLE OBJECT AFTER---
BookOfSecrets [immutableString=The hacker community may be small, but it's skills are driving the future. {1865026736}
, immutableInteger=321 {321}
, immutableDouble=876.54 {-1425114391}]

Java Reflection Examples

  • Class Example
    Simple example shows you how to dynamically load and instantiate a class, get class name, package name, and superclass name.
  • Modifier Example
    Showing you how to get information about classes, fields and methods using modifier method.
  • Method Example
    Providing full details of how to get all methods, and invoke methods using reflection.

Frequently Asked Questions on Java Reflection API (FAQs)

Please Share Us on Social Media

Facebooktwitterredditpinterestlinkedinmail

Leave a Reply

Your email address will not be published. Required fields are marked *