Accessing Java Private Attributes and Methods
July 31, 2009
Filed under Java/Maven
Tags: attribute, encapsulation, java, junit, method, private, reflection
Java reflection is a very powerful framework.
Sometimes its power seems outside the usual Java methodology.
For example, I needed to manually change a private variable in one of the classes I was testing.
Java reflection has an API that allows this. Of course, the API does not know if one is in a test or production source code. It works anywhere!
Below is an example of three classes.
- IntWrapper is the class under question.
- IntWrapperTest is the JUnit class
- RegularMainClass is a class with a main method, almost a copy of IntWrapperTest.
It is worth noting that the testers and the testee are in separate packages. So one can change the private variable or call a private method of any class in any package!
package com.otherexample;
public class IntWrapper {
private int i;
public IntWrapper(int i) {
this.i = i;
}
private void incrementI() {
i++;
}
public int getI() {
return i;
}
}
package com.example;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.junit.Test;
import com.otherexample.IntWrapper;
import static org.junit.Assert.*;
public class IntWrapperTest {
@Test
public void testPrivateMembers() {
try {
IntWrapper iw = new IntWrapper( 15 );
assertEquals(15, iw.getI() );
Field f = iw.getClass().getDeclaredField("i");
f.setAccessible(true);
f.setInt(iw, 18);
assertEquals(18, iw.getI() );
Method m = iw.getClass().getDeclaredMethod("incrementI");
m.setAccessible(true);
m.invoke(iw);
assertEquals(19, iw.getI() );
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
package com.example;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import com.otherexample.IntWrapper;
public class RegularMainClass {
public static void main(String[] args) {
try {
IntWrapper iw = new IntWrapper( 15 );
if( 15 != iw.getI() ) System.err.println("Assertion failed");
else System.out.println("all is well 1");
Field f = iw.getClass().getDeclaredField("i");
f.setAccessible(true);
f.setInt(iw, 18);
if( 18 != iw.getI() ) System.err.println("Assertion failed");
else System.out.println("all is well 2");
Method m = iw.getClass().getDeclaredMethod("incrementI");
m.setAccessible(true);
m.invoke(iw);
if( 19 != iw.getI() ) System.err.println("Assertion failed");
else System.out.println("all is well 3");
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
Ignoring Java Generics Safety
July 24, 2009
Filed under Java/Maven
Tags: data structures, generics, java, workaround
Java Generics (something like List <String>) provides a very nice way to group similar operation on different types of objects.
There is a way around it though.
In C++ the template invocation actually causes a new instance of the underlying type to be created and compiled. Java only creates the one type instance and then uses the generics as a variable when performing the type checks. Generics allow the compiler to enforce more type safety checks.
More type safety is a brilliant idea and I really like generics, so I have no practical use for this work around to fool the compiler safety checks. Let me know if you find one.
import java.util.ArrayList;
import java.util.Collection;
public class Q1{
public static void main( String[] args ) {
ArrayList<Integer> is = new ArrayList<Integer>();
// list is [ ]
is.add(2);
// list is [ 2 ]
Collection<?> ac = is;
ac.add( null );
// null is the only value that can be added to the ac list.
// list is [ 2, null ]
(( ArrayList<String> ) ac).add("andrejserafim");
// the above will give you an unchecked cast warning,
// but will compile and work
// list is [ 2, null, "andrejserafim" ]
}
}
Initially the list is tagged with ArrayList<Integer>.
The cast to Collection<?> wipes off that tag.
Then ArrayList<String> cast writes a new tag onto the list now stating it’s a list of strings.