Sriram
Sriram Creator of geeky things, mostly unused junk

Changing static final fields in Java for JUnit Unit Tests

Reflection is a helluva drug

Reflection allows you to tinker with Java in ways that are clearly against everything you’ve learnt from your Object Oriented Programming lessons.

Reflection is like being handed a master-key. I’ve used it to come up with solutions to problems that didn’t have an easier or cleaner way in Java - cutting down cumbersome code writing.

Minor disclaimer: Don’t do this! You might open Pandora’s Box.

If you still want to go ahead, read on.

Recently for a project we needed a way to test some code which depended on a class which had public static final constants that are picked up from a property file at runtime. The problem was that there was no way to unload the class and let Java reload the static final fields. I checked up about writing my own classloader but it seemed like overkill. All I wanted was to be able to change the values of the static final fields to run more tests, without having to worry about changing the code too much.

The solution came to me in the form of Reflection. Using Reflection, you can read values of fields that are private or even final, and write back into them! (Lesser demons scream in my head…)

So, without further ado, the unethical way to edit static final fields in Java that makes people burned by Reflection wince, is as follows:

First, we get the field to tinker with:

1
Field field = clazz.getDeclaredField( fieldName );

Somehow I wasn’t able to use the getField() method for this, and had to use the getDeclaredField() method instead.

Next we modify the field using reflection to allow editing final fields (Sort of like self-immolation):

1
2
3
4
Field modifiersField = Field.class.getDeclaredField( "modifiers" );
boolean isModifierAccessible = modifiersField.isAccessible();
modifiersField.setAccessible( true );
modifiersField.setInt( field, field.getModifiers() & ~Modifier.FINAL );

Now we allow the field to be edited by setting the accessible field to true:

1
2
boolean isAccessible = field.isAccessible();
field.setAccessible( true );

Now do the deed. Set the value to the null object to affect the static member:

1
field.set( null, value );

Clean up by changing the accessible field back:

1
2
field.setAccessible( isAccessible );
modifiersField.setAccessible( isModifierAccessible ); Might not be very useful resetting the value, really. The harm is already done.

Note: Compilers optimize constants by replacing them inline, which will make changing the constant useless. For example, assume changeField is our method which changes the static final field TEST of Class clazz:

1
2
3
4
5
6
7
8
public static final String TEST = "Hello";

public static void main( String[] args )
{
    System.out.println( TEST ); // prints "Hello"
    changeField( clazz, "TEST", "hi!" );
    System.out.println( TEST ); // prints "Hello"
}

This is because during compilation, your compiled decided to replace System.out.println( TEST ); with System.out.println( "Hello" );

You can overcome this behavior by setting the value of the static final field using a method such as:

1
public static final String TEST = PropertyFileReader.getProperty("TEST");

This prevents the compiler from optimizing the code, allowing you to tinker with it using Reflection.

Statiflex:

If you don’t want to do it yourself, and instead tell people you “found a library on the Internet that does it”, here’s a link to my repository containing a jar that will let you do the same: Statiflex

You can add it as a Maven dependency like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
<dependency>
    <groupId>com.caffinc</groupId>
    <artifactId>statiflex</artifactId>
    <version>1.0</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/src/main/resources/jars/statiflex-1.0.jar</systemPath>
</dependency> `Statiflex` depends on `SLF4J` with `Log4j`:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.12</version>
</dependency>

Usage:

1
boolean success = Statiflex.flex(MyClass.class, "MY_STATIC_FINAL_FIELD", "NEW VALUE");

Feel free to tinker with my code and use it in your projects. Don’t blame me if you end up with issues :) Happy hacking!

comments powered by Disqus