I have been burnt by this issue recently, and at the risk of getting a bunch of negative votes and wrath of the Java community, I would suggest NOT to rely on Java access - there is nothing private about a class variable or a method (or what I used to think "private" to be for encapsulation in an Object Oriented Programming language. Just use public for everything and sleep well at night. Here is an explanation, Consider the following class:
package hiddenscope;
public class Privacy {
private int privateInt = 12;
private double oneBy2pi = 0.5 / Math.PI;
private String myClassName;
public Privacy () {
privateInt = 12;
myClassName = this.getClass().getName();
print ("Constructor");
}
// Given the circumference of a circle, get its radius
public double c2r (double c) {
return c * oneBy2pi;
}
// Do not let any other class call this method
private void print (String caller) {
System.out.println ("["+caller+"] Current value of privateInt in class " +
myClassName + " is " + privateInt);
}
}
Here is a class that changes the private variable privateInt of the above class and calls it private "print" method, even from outside the package (protected, anyone?):
package userpackage;
import hiddenscope.Privacy;
import java.lang.reflect.*;
public class EditPrivate {
private int i2set;
private String fieldNameToModify, myClassName;
private Object p;
private Method printer;
public EditPrivate (Object object, String fName, int value) {
fieldNameToModify = fName;
i2set = value;
p = object;
myClassName = this.getClass().getName();
Method[] methods = p.getClass().getDeclaredMethods();
for (Method m : methods) {
if (m.getName().equals("print")) {
printer = m;
printer.setAccessible(true);
break;
}}
if (printer == null)
return;
try {
printer.invoke (p, myClassName);
} catch (IllegalAccessException ex1) { ex1.printStackTrace();
} catch (InvocationTargetException ex2) { ex2.printStackTrace();
}}
public void invadePrivacy () throws Exception {
Field[] fields = p.getClass().getDeclaredFields();
for (Field field : fields) {
String fieldName = field.getName();
if (fieldName.equals(fieldNameToModify)) {
field.setAccessible(true);
if ("int".equals(field.getType().toString())) {
try {
field.setInt(p, i2set);
if (printer != null)
printer.invoke (p, myClassName);
return;
} catch (IllegalAccessException ex) {
ex.printStackTrace();
}}}}
throw new Exception ("No such int field: " + fieldNameToModify);
}
public static void main(String[] args) {
try {
Privacy p = new Privacy();
new EditPrivate(p, "privateInt", 15).invadePrivacy();
} catch (Exception ex) {
ex.printStackTrace();
}}}
To add insult to this injury, this is what happens where multiple threads mess up the internal and private variables of a class (oneBy2pi is supposed to be private!)
package userpackage;
import java.lang.reflect.Field;
import java.util.concurrent.*;
import java.util.*;
import hiddenscope.*;
public class ThreadedPi implements Runnable {
Privacy p = new Privacy();
private int nSample = 2000, bins[] = new int[4]; // 94814117 999065 0003379, 2028859
private Field c2dField;
private boolean mutate;
public ThreadedPi () {
Field[] fields = p.getClass().getDeclaredFields();
for (Field field : fields) {
String fieldName = field.getName();
if (fieldName.equals("oneBy2pi")) {
field.setAccessible(true);
c2dField = field;
}}}
synchronized private boolean setMutationOfField (boolean master, boolean value) {
if (master)
mutate = value;
return mutate;
}
public void run () {
Random rng = new Random();
for ( ; ; ) {
if (setMutationOfField (false, true)) {
int nDigit = 2 + rng.nextInt(4);
double doublePi = 4.0 * Math.atan(1.0),
decimal = Math.pow(10, nDigit),
apprxPi = Math.round(decimal * doublePi) / decimal;
try {
c2dField.setDouble (p, 0.5/apprxPi);
} catch (IllegalAccessException ex) {
ex.printStackTrace();
}} else {
return;
}}}
public void execute () {
setMutationOfField (true, true);
new Thread (this).start();
Executed[] class2x = new Executed[nSample];
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 0 ; i < 4 ; i++)
bins[i] = 0;
for (int i = 0 ; i < nSample ; i++) {
class2x[i] = new Executed();
executor.execute(class2x[i]);
double d = class2x[i].getDiameter(-1);
if (d < 399.99) bins[0]++;
else if (d < 400) bins[1]++;
else if (d < 400.01) bins[2]++;
else bins[3]++;
//System.out.println ("d: " + d);
}
setMutationOfField (true, false);
for (int i = 0 ; i < 4 ; i++)
System.out.print("\t\t[" + (i+1) + "] " + bins[i]);
System.out.println ();
}
public static void main(String[] args) {
ThreadedPi tp = new ThreadedPi();
for (int k = 0 ; k < 5 ; k++)
tp.execute();
}
private class Executed implements Runnable {
private double d=-1, c = 2513.274123;
synchronized double getDiameter (double setter) {
if (setter < 0) {
while (d < 0) {
try {
wait();
} catch (InterruptedException ex) {
}}} else {
d = setter;
notify();
}
return d;
}
public void run () {
getDiameter (p.c2d(c));
}}}
All the above classes compile and run perfectly, which explains why "private" may not have any meaning in java. Shields up, Scotty!