In Java, and maybe also in other languages, for example in a getter you have to decide if you want to return a reference to something or a clone (copy).
return myInstance;
Just returning the reference is very fast and needs no additional memory but modifications of an instance get "written back" the the original one.
return myInstance.clone();
Returning a clone needs time and doubles the memory for that variable but keeps it safe.
It is possible to create an immutable view on something:
return MyUtil.immutableView(myInstance);
but then sometimes I want to modify it, just not to have it written back.
Now my idea is, is it possible (or already done, or is there a programming language that does it) to create an object that is initially a reference to something as long as there are no modifications. As soon as the first modification begins, the reference would update itself to a clone.
Something like this:
Class<T> CloneReference
{
T ref;
boolean cloned=false;
public CloneReference(T ref) {this.ref=ref;}
T getForReadOnly()
{
return ref;
}
T getForReadWrite()
{
if(!cloned) ref=ref.clone();
return ref;
}
}
Unfortunately, this solution is complicated, clumsy and easy to break (calling getForReadOnly() and then using instance changing operations). Is it possible to do better or is that just not possible with Java?
What you're looking for sounds pretty much like Copy-On-Write. I remember that PHP is a language which did implement this.
I think it should basically be possible to implement COW in Java as well. I think of returning some proxy which is initialized with the original instance. On the first write access the proxy will continue using a copy. Here's a SSCCE:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.junit.Test;
import static org.junit.Assert.*;
public class CowSSCCE {
public interface Bean {
public String getName();
public void setName(String name);
public Object clone();
}
public class BeanImpl implements Bean {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Object clone() {
BeanImpl copy = new BeanImpl();
copy.name = new String(name);
return copy;
}
}
public class COWInvocationHandler implements InvocationHandler {
private Bean instance;
private boolean copy = false;
public COWInvocationHandler(Bean instance) {
this.instance = instance;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// copy only on the first setter call.
if (!copy && method.getName().startsWith("set")) {
instance = (Bean) instance.clone();
copy = true;
}
return method.invoke(instance, args);
}
}
@Test
public void testCOW() {
Bean original = new BeanImpl();
original.setName("original");
Bean reference = (Bean) Proxy.newProxyInstance(
Bean.class.getClassLoader(), new Class[] { Bean.class },
new COWInvocationHandler(original));
// no write access, reference is pointing to the original instance
assertEquals(original.getName(), reference.getName());
assertEquals(original.toString(), reference.toString());
// write access, reference is a copied instance
reference.setName("reference");
assertEquals("reference", reference.getName());
assertNotEquals(original.getName(), reference.getName());
assertNotEquals(original.toString(), reference.toString());
}
}
As someone mentioned readability, this shouldn't be an issue: Write an advice for e.g. the annotation @ReturnCopyOnwriteReference which replaces transparently the returned object with the proxy. An API method which returns such a proxy needs only that annotation:
@ReturnCopyOnwriteReference
public Bean getExpensiveBean() {
return originalBean;
}
If you're just looking for a COW collection use Java's CopyOnWriteArrayList.
Look at Scala programming language. It runs in JVM, and variables in most cases are immutable.
In Java there is a java.util.Collections#unmodifiableCollection() method, which wraps any collection into unmodifiable collection. This prevents it from editing. But I did not saw or think of any use case which would provide your desired behavior.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With