Reputation: 821
I don't want to do the deep copy way.
Say, I have a field of some mutable type, a x,y,z Coordinate for example. Occasionally, I need to expose this field to some viewers. And I want it be read-only. I remember reading something like a wrapper to do these kind of stuff, but I don't remember the details.
The x,y,z Coordinate example may be too simple because x,y,z are primitive type. So getX() always return a copy.
I want a general solution even if the x,y,z fields are of yet another mutable type.
Can anybody help?
EDIT:
public class Client
{
public static final Holder holder = new Holder();
public static void main(String[] args)
{
UserWrapper user = holder.getUser();
System.out.println(user); //UserWrapper{user=User{address=Address{street='street 101'}}}
user.getAddress().setStreet("mars"); //UserWrapper{user=User{address=Address{street='mars'}}}
System.out.println(user);
}
}
public class Holder
{
private User user;
public Holder()
{
user = new User();
Address address = new Address();
address.setStreet("street 101");
user.setAddress(address);
}
public UserWrapper getUser()
{
return new UserWrapper(user);
}
}
public class User
{
private Address address;
public Address getAddress()
{
return address;
}
public void setAddress(Address address)
{
this.address = address;
}
}
public class UserWrapper
{
private User user;
public UserWrapper(User user)
{
this.user = user;
}
public Address getAddress()
{
return user.getAddress();
}
}
EDIT:
credit to I don't know who
(he deletes the answer), I find this link he mentioned in his original post very helpful.
Upvotes: 1
Views: 155
Reputation: 3778
There is no built-in mechanism in Java that will enable you to do that. Usually, if you move instances around, you'd either:
Since you don't want/can't choose either of these ways, you'll need to use an alternative. There are a lot of different ways to implement this depending on your requirements and how complex is your class structure, but the general approach would be to publish an immutable wrapper instead of the original.
Here are some examples:
public class XYZ {
public int x, y, z;
}
public class XYZWrapper {
private XYZ xyz;
public XYZWrapper(XYZ xyz) {
this.xyz = xyz;
}
public int getX() { return x; }
public int getY() { return y; }
public int getZ() { return z; }
}
public class Address {
public String name;
public XYZ xyz;
}
public class AddressWrapper {
private String name; // Note that this could be public since any String is immutable
private XYZWrapper xyzWrapper;
public AddressWrapper(String name, XYZ xyz) {
this.name = name;
this.xyzWrapper = new XYZWrapper(xyz);
}
public String getName() {
return name;
}
public XYZWrapper getXYZWrapper() {
return xyzWrapper;
}
}
Now, if instead of XYZ
and Address
classes, you work with interfaces, you can have 2 implementations (e.g. XYZMutable
& XYZImmutable
) which will allow you to abstract which type of class you're returning, and also will enable you to create an instance of XYZImmutable
from an instance of XYZMutable
(assuming that the interface defines only & all getter methods).
One more note about this approach (especially if you do it the preferred way by using interfaces): Even if you have a complex class hierarchy, you can do this relatively effortlessly by creating a generator class that receives an interface instance, a mutable implementation instance and returns an immutable implementation instance as the return value.
Upvotes: 2
Reputation: 11642
Perhaps you're thinking of the "copy on write" idiom. This allows you to avoid copying unless you have to. It's use is generally not recommended because it is not thread-safe unless you use synchronization which will unnecessarily slow down single-threaded applications.
It works by keeping a reference count of its internal data; something like this untested bit of code:
public class User
{
private int addressReferenceCount;
private Address address;
public User(Address address) {
addressReferenceCount = 0;
this.address = address;
}
public Address getAddress() {
addressReferenceCount++;
return address;
}
public void setAddress(Address address)
{
if (addressReferenceCount == 0) {
this.address = address;
}
else {
this.address = new Address(address);
addressReferenceCount = 0;
}
}
}
This ensures that user code like this will get different addresses when necessary:
User u = new User(new Address("1 Acacia Avenue"));
Address oldAddress = u.getAddress();
Address stillOldAddress = u.getAddress();
u.setAddress(new Address("2 Acacia Avenue"));
Address newAddress = u.getAddress();
assert (oldAddress == stillOldAddress); // both refer to same object
assert (oldAddress != newAddress);
Upvotes: 1
Reputation: 70949
The traditional ways:
There's others, but this should get you started.
Upvotes: 2