Context :
- package data, public :
public interface _Data {
public String getData();
}
public class _PackageAPI {
DataHolder holder;
public void createHolder(String data) {
holder = new DataHolder();
holder.setData(data);
}
public void mutateHolder(String data) {
holder.setData(data);
}
public _Data getSnapshot() {
return DataSnapshot.from(holder.getData());
}
public _Data getReader() {
return holder.readOnly();
}
}
- package data, package-private :
class DataHolder {
private String data;
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public _Data readOnly() {
return new _Data() {
@Override
public String getData() {
return DataHolder.this.data;
}
};
}
}
class DataSnapshot {
public static _Data from(String data){
return new _Data() {
@Override
public String getData() {
return data;
}
};
}
}
- sample client usage :
package clientPackage;
import data._Data;
import data._PackageAPI;
public class ExampleRunner {
public static void main(String[] args) {
_PackageAPI handler;
System.out.println("Creating...");
handler = new _PackageAPI();
handler.createHolder("INITIAL DATA");
System.out.println("Done creating...");
_Data initialSnapShot = handler.getSnapshot();
_Data initialReader = handler.getReader();
System.out.println("initialSnapShot holds :" + initialSnapShot.getData() );
System.out.println("initialSnapShot class :" + initialSnapShot.getClass() );
System.out.println("initialReader class :" + initialReader.getClass() );
System.out.println("initialReader holds :" + initialReader.getData() );
System.out.println("Mutating...");
handler.mutateHolder("MUTATED DATA");
_Data subsequentSnapShot = handler.getSnapshot();
_Data subsequentReader = handler.getReader();
System.out.println("Done mutating...");
System.out.println("initialSnapShot holds :" + initialSnapShot.getData() );
System.out.println("initialReader holds :" + initialReader.getData() );
System.out.println("subsequentSnapShot holds :" + subsequentSnapShot.getData() );
System.out.println("subsequentReader holds :" + subsequentReader.getData() );
}
}
- And console output:
Creating...
Done creating...
initialSnapShot holds :INITIAL DATA
initialSnapShot class :class data.DataSnapshot$1
initialReader class :class data.DataHolder$1
initialReader holds :INITIAL DATA
Mutating...
Done mutating...
initialSnapShot holds :INITIAL DATA
initialReader holds :MUTATED DATA
subsequentSnapShot holds :MUTATED DATA
subsequentReader holds :MUTATED DATA
FIRST QUESTION : given getSnapshot() returns a _Data (of class : DataSnapshot$1) whose method getData() returns the "real" data reference, ie the content of the variable data of the DataHolder object, is this safe or is it somehow possible to mutate DataHolder leveraging access to this reference? If yes, how ?
FIRST QUESTION SHORTENED : is it anyhow possible to mutate content of the memory referenced by a reference, using only the reference ?
(Of course solution to this is to clone the String being referenced.)
SECOND QUESTION : is there anyway to mutate a DataSnapshot$1 (the "immutable" version of _Data) instance ?
THIRD QUESTION : given DataHolder$1 (the "readOnly" version of _Data) holds internally a reference to the DataHolder instance providing it, is it safe to expose such a DataHolder$1, or is there anyway to mess with the DataHolder instance from the DataHolder$1 object ?
EDIT : I would have put a paranoïd tag if there was one