33

In C++, I can define an accessor member function that returns the value of (or reference to) a private data member, such that the caller cannot modify that private data member in any way.

Is there a way to do this in Java?

If so, how?

I know about the final keyword but AFAIK, when applied to a method it:

  1. Prevents overriding/polymorphing that method in a subclass.
  2. Makes that method inline-able. (see comment by @Joachim Sauer below)

But it doesn't restrict the method from returning a reference to a data member so that it can't modified by the caller.

Have I overlooked something obvious?

ef2011
  • 10,431
  • 12
  • 49
  • 67
  • 1
    Your item #2 is wrong: inlining is not specified by Java, that kind of optimization is left entirely for the JVM to decide and `final` is not necessary for the JVM to inline a method. – Joachim Sauer May 04 '11 at 16:11
  • @Joachim Sauer Thanks for pointing this out. I am confused. The following http://www.roseindia.net/javatutorials/final_methods.shtml states the opposite. Is he wrong? – ef2011 May 04 '11 at 17:25
  • 2
    Yes, it is wrong. Roseindia.net is a resource with generally bad quality. It's sites range from slightly misleading to outright wrong. While `static final` fields that are initialized with a constant expression (so called "compile time constants") can be inlined, no method inlining of any kind is specified in Java. It's a possible runtime optimization of the JVM at runtime, but it must not effect the observed behaviour (which is easier to do with `final` methods, but possible with non-`final` ones as well). – Joachim Sauer May 04 '11 at 17:27
  • @Joachim Sauer Thanks. +2 and I will edit my original post to reflect your correction. – ef2011 May 04 '11 at 17:33
  • 1
    Since I've found some confusing information on the web, I'll have to partially correct myself: there is at least [one compiler](http://publib.boulder.ibm.com/infocenter/iseries/v5r4/index.jsp?topic=%2Frzaha%2Fjmiperf.htm) that actually inlines methods. Generally my statement is still true: normal Java compilers don't inline methods, but leave that decision to the runtime. – Joachim Sauer May 04 '11 at 17:38

9 Answers9

24

There's no equivalent to the C const "type modifier" in Java (sadly).

The closest you can get is to return an immutable object or an immutable wrapper around a mutable object.

Immutability is not a language feature of Java, however, so you'll have to rely on Libraries.

Examples of immutable objects are:

  • the primitive wrappers Integer, Character, ..
  • String
  • File
  • URL

Commonly used immutable wrapper (i.e. wrappers around mutable types that prevent mutation) are those returned by the Collecton.unmodifiable*() methods.

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
  • 4
    These are two things I would like to see in Java: an equivalent to "const" and a way of declaring an object as immutable so that the compiler can detect potential violations. [Annotations](http://stackoverflow.com/questions/203475/how-do-i-identify-immutable-objects-in-java/204015#204015) might at least help with the latter. – Ben May 04 '11 at 16:18
  • Note, It's not the same as "const method". I still can modify object state while getting immutable data – JustAnotherCoder May 12 '20 at 12:53
8

This does not exist in java. final and const have different semantics, except when applied to a variable of a primitive type. The java solution typically involves creating immutable classes - where objects are initialized in construction and provide no accessors allowing change. Example of such classes would be e.g. String or Integer.

Erik
  • 88,732
  • 13
  • 198
  • 189
5

You either return an immutable object, or return a copy of the private instance variable. This way, the object's internal state is "protected" from modification, i.e.:

private MyMutableObject mutable = ...

public MyMutableObject getMutableObject() {
   return new MyMutableObject(this.mutable);
}
`
Lefteris Laskaridis
  • 2,292
  • 2
  • 24
  • 38
  • Lots of good answers here but you gave a link a "Strategy for Defining Immutable Objects" which seems to me the only sane way out (I don't want to use external libraries for that). So you get the accept. :-) – ef2011 May 04 '11 at 17:27
4

You haven't overlooked anything. There is no way in pure Java to do so. There might be libraries which provide some subset of this using annotations, but I don't know any offhand.

The way you pass back a reference to immutable data is to make the class you pass back immutable, plain and simple. There are a couple of library functions to help you produce an immutable view of some data in some very limited but common cases. Here's one example:

private List<String> internalData;

public List<String> getSomeList() {
    return Collections.unmodifiableList(internalData);
}
Mark Peters
  • 80,126
  • 17
  • 159
  • 190
2

You could return a copy of the member, thus changes will not be reflected in the object the private reference points to. With primitives, of course, this problem doesn't exist.

Be mindful of memory usage, however! This might not be the right solution for all situations. In that case, an immutable object as suggested in another answer might be the way to go.

no.good.at.coding
  • 20,221
  • 2
  • 60
  • 51
1

I don't think there is a way to that in Java for non primitive objects (you're always passing around references to such objects). The closest you could do would be to return a copy of the object (using clone or something like that) ; but that would not be very idiomatic Java.

I you want to give access only to the 'visible' part of a member object, what you could do is create an interface with the visible part, and return this interface. For example :

public interface Bar {
     public int getBing();
}

public class BarImpl implements Bar {
     private int bing;
     public int getBing() {
        return bing;
     }
     public void setBing(int bing) {
        this.bing = bing;
     }
}

public class Foo {
     private BarImpl bar;

     public Bar getNonModifiableBar() {
         return bar; // Caller won't be able to change the bing value, only read it.
     }
}
phtrivier
  • 13,047
  • 6
  • 48
  • 79
  • Why would returning a copy not be idiomatic Java? See http://www.informit.com/articles/article.aspx?p=31551&seqNum=2 – helpermethod May 04 '11 at 16:14
  • Sorry, what I mean is that for accessors (the infamous get/set methods) it is not the most common thing to do. I would not expect a function called getFoo to return a copy of a field rather than a reference to the field itself. In Boch case, the interface is not getStartDate() / setEndDate() so there would be less potential misunderstaning. – phtrivier May 04 '11 at 16:21
0

It's up to the returned object to prevent modification. Java doesn't provide declarative/compile-time checking of unmodifiable objects except to the extent that the type lacks mutators.

There is some support in the JDK: methods like Collection.unmodifiableCollection will create an object that will throw runtime exceptions if the client calls collection mutator methods.

If you're truly motivated, you can get compile-time checks by defining read-only interfaces (or classes) that only expose/implement accessor methods on your own. Keep in mind that merely declaring that a method returns a read-only interface will not prevent runtime modification if the client uses introspection and the object provides mutators (that don't throw UnsupportedOperationException).

John
  • 1,322
  • 14
  • 26
  • 1
    By the way, the unmodifiable... methods don't copy the subject collection. The new object is a proxy that only delegates the accessor calls to the original object and throws an exception on any mutator call. – John May 04 '11 at 16:18
0

One way to avoid this issue is to no expose the data structure (another is returning a copy, or an immutable wrapper)

e.g. instead of

public List<String> getSomeList();

have

public String getSomeElement(int index);
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
0

When you do something like this:

Object2 obj2 = obj1.getObj2();
obj2 = new Object2();

The original private member (obj1.obj2) remains as it were before (just to be sure that you grasped that concept). You can just omit the setter to obj2 so that the inner field cannot de changed.

If you want Object2 fields to be immutable you will need to apply the same pattern (private fields, no setters).

This answer your question?

Anthony Accioly
  • 21,918
  • 9
  • 70
  • 118