3

I have an object such as

public class ABC {

    private String a;
    private String b;
    private String c;

    //getters and setters    
}

This object is returned from a method in the collections such as ArrayList<ABC>.

I just want to make the return immutable without changing anything in the object. Can anyone please help me with this?

Roman C
  • 49,761
  • 33
  • 66
  • 176
gagan
  • 173
  • 1
  • 3
  • 11
  • I like [@MurtazaZaidi's answer](http://stackoverflow.com/a/26259984/1392132) but if you absolutely must have your setters, return a copy of the object. – 5gon12eder Oct 08 '14 at 14:55
  • There is nothing in Java that supports what you want (making an object immutable without changing the class / writing the class to be immutable in the first place) – Erwin Bolwidt Oct 08 '14 at 14:55
  • @Erwin.. can't I return the copy of the object? – gagan Oct 08 '14 at 14:56
  • @gagan sure but that copy would be mutable. Unless you copied all the data into an object of a second class that is written to be immutable. – Erwin Bolwidt Oct 08 '14 at 14:58
  • @Erwin.. the only way is to change my class. RIght? I have to make the fields in the class final.. and the class itself too.. but I can still have getters and setters? – gagan Oct 08 '14 at 15:03

3 Answers3

4

Don't provide setters (mutators), make immutable attributes private, only provide value assignment via constructor.

You can always declare your immutable attributes final. So you can only assign them values once and can't change them later.

Murtaza Zaidi
  • 599
  • 3
  • 14
  • 1
    I don't want to change my object. The method returns ArrayList . Can I just make my return immutable? – gagan Oct 08 '14 at 14:53
  • 1
    Shouldn't he use the `final` modifier on attributes? That surely ensures that the fields are immutable. – Erwin Bolwidt Oct 08 '14 at 14:54
  • @gagan It is not possible that for one object some fields shall remain immutable and for other object of same class they are not. You can make another class with similar members, make its attributes final and pass in its constructor the values from object of this class. – Murtaza Zaidi Oct 08 '14 at 15:02
  • @Murtaza... so I will have to change my class and make the fields final.. and class too? – gagan Oct 08 '14 at 15:05
  • You problem is you don't want to make your class immutable but return the image of its object which remains immutable. Solution is make two similar classes. But one immutable, other not. Use immutable class's object to return. You get it? – Murtaza Zaidi Oct 08 '14 at 15:10
2

Use interfaces with only getters

A is your concrete (impl) class

Coding to interfaces?

public I getA(){ retrun AImpl();}

where

public interface I { public String getOne()}
public AImple implements I {...}

The only "Change" in your current class would be "implements I"

JDK and Apache commons use decorators

http://grepcode.com/file/repository.jboss.org/nexus/content/repositories/releases/org.jboss.embedded/thirdparty-all/beta3.SP15/org/apache/commons/collections/list/UnmodifiableList.java

Another solution

Clone your object and return it, that way copy is changed and original object remains intact

Community
  • 1
  • 1
Kalpesh Soni
  • 6,879
  • 2
  • 56
  • 59
  • Such an approach isn't safe, since nothing would prevent code from calling `getA()`, casting the returned type to the actual type of the instance, and manipulating it at will. – supercat Dec 22 '14 at 18:55
  • 1
    if your computer is running, you can write another program to go look up the objects memory and change 0s to 1s, this is not about safety – Kalpesh Soni Dec 27 '14 at 01:02
2

You cannot make an object immutable if its class provides for mutation. Objects always offer all the capabilities defined by their classes.

Therefore, if you want an immutable object then you need an immutable class. If you cannot change the class in question, then a wrapper class such as @duffymo described could serve that purpose. Note, however, that objects of such a class are not interchangeable with objects of the wrapped class, and also that somehow you need to provide for applying the wrappers.

If you need objects that are fully interchangeable with objects of class ABC, then you're stuck with the fact that ABCs are mutable, therefore anything interchangeable with ABCs is mutable, at least with respect to the mutable aspects of ABC. Then it comes down to why you want immutability. If the point is to avoid mutating the object referenced by the List, then copying those objects (to whatever depth is appropriate) is an alternative.

As a third alternative, if the target class has no non-private fields then you might be able to create a subclass, overriding the setters to be ineffective or to throw some variety of unchecked exception. In that case, note that

  1. Such a subclass is not good form, and its instances are not truly interchangeable with instances of class ABC.
  2. If class ABC has accessible properties of mutable types (e.g. mutable containers), then you may need to do something to prevent those objects from being mutated, too. Recursively.
  3. Yes, this is a big mess.
John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • you (almost) could if you use interfaces – Kalpesh Soni Oct 08 '14 at 15:18
  • @John.. thanks.. I have a method that returns ArrayList of ABC. I wanted to make that result immutable. I got it now.. I can't make it immutable unless the class is immutable. – gagan Oct 08 '14 at 15:28
  • http://docs.oracle.com/javase/7/docs/api/java/util/Collections.html#unmodifiableList(java.util.List) will make sure List itself is not modifiable, dont return ArrayList – Kalpesh Soni Oct 08 '14 at 15:39
  • @Kaplesh.. will this work.. Collections.unmodifiableCollection(results); if results is the collection of ABC – gagan Oct 08 '14 at 15:46
  • @Kaplesh yes, if the declared element type of the `List` were an interface type that provided no setters, then consumers of the `List` could not modify elements without knowing the actual element class and casting. It's an excellent approach, but it does not make the elements genuinely immutable. Also, it may not be feasible to change the type parameters of the `List`. – John Bollinger Oct 08 '14 at 15:54
  • @Kalpesh Collections.unmodifiableList() will provide a `List` wrapper through which elements can neither be added to nor removed from the underlying `List`. That in no way makes the elements themselves immutable, and it doesn't by itself even prevent the original List from being modified directly. – John Bollinger Oct 08 '14 at 15:56
  • Use interfaces instead of class (with only getters) to put in the List? – Kalpesh Soni Oct 08 '14 at 16:15