17

Using immutable objects has become more and more common, even when the program at hand is never meant to be ran in parallel. And yet we still use getters, which require 3 lines of boilerplate for every field and 5 extra characters on every access (in your favorite mainstream OO language). While this may seem trivial, and many editors remove most of the burden from the programmer anyways, it is still seemingly unnecessary effort.

What are the reasons for the continued use of accessors versus direct field access of immutable objects? Specifically, are there advantages to forcing the user to use accessors (for the client or library writer), and if so what are they?


Note that I am referring to immutable objects, unlike this question, which refers to objects in general. To be clear, there are no setters on immutable objects.

Community
  • 1
  • 1
Alex DiCarlo
  • 4,851
  • 18
  • 34
  • There's an exact duplicate of this question from a couple of weeks ago. I may try to find it. – David Titarenco Jan 11 '13 at 05:47
  • @DavidTitarenco I'd be interested to see it. – Alex DiCarlo Jan 11 '13 at 05:48
  • 2
    @Cthulhu One could argue that it actually is not uniform. Consider a system where every mutable field was hidden behind accessors while every immutable was not. Then wherever one sees a getter, they know they are dealing with mutable state, while direct field access means it is immutable. As it stands, neither can be inferred if both are hidden behind a getter. – Alex DiCarlo Jan 11 '13 at 05:50
  • How do you enforce immutability with direct field access (at least in Java, there is no way). And are you talking only about the syntax, or the runtime consequences as well? – Thilo Jan 11 '13 at 05:50
  • It's not the one linked above, but it had some similar points. – David Titarenco Jan 11 '13 at 05:50
  • 1
    Indeed, I saw that post, but that one refers to objects in general, and most if not all of the points relate to mutable objects, not immutable, I'll edit the OP to link it. – Alex DiCarlo Jan 11 '13 at 05:52
  • The distinction is non-relevant. If your object is immutable, you ought to merely use a getter. See Jon Skeet's answer here: http://stackoverflow.com/questions/2815493/am-i-doing-getters-setters-the-right-way-in-java – David Titarenco Jan 11 '13 at 05:54
  • @Thilo Declare fields to be `final` – Alex DiCarlo Jan 11 '13 at 05:54
  • 1
    So are you happy with Objective-C properties, where the setter/getter looks like a field access in source code? Is this question only about syntax? – Thilo Jan 11 '13 at 05:56
  • 1
    @dicarlo2: `final` fields only work to enforce immutability if the referenced object is also immutable. Not an ArrayList for example. – Thilo Jan 11 '13 at 05:57
  • @Thilo This is true, although some might argue that the object is not truly immutable if it requires defensive copies. Also, as to Objectve-C properties, yes, thought I don't know the specifics of it I'm sure it's similar to Scala. – Alex DiCarlo Jan 11 '13 at 06:01
  • 3
    Voting to reopen. This question is not an exact duplicate -- the question of immutability adds an important distinction. – Daniel Pryden Jan 11 '13 at 06:08
  • @DavidTitarenco "Getters and setters are better than public fields" from a high rep user (even Jon Skeet) is hardly a reason to take at face value as a rule to never be broken or reconsidered. – Alex DiCarlo Jan 11 '13 at 06:20
  • 2
    This is actually very language-dependent; it's not just an OOP issue... if this is reopened I'll probably post an answer explaining why I think it's not set in stone. – user541686 Jan 11 '13 at 06:34
  • @Mehrdad I'd be very interested to see it, hopefully you get the opportunity to post it. – Alex DiCarlo Jan 11 '13 at 06:39
  • It's ok to expose public final fields in many use cases. Mostly when refactor is not a concern. For example, this is an in-house class, so we can freely change it. Or, this class is such a simple struct, we don't expect to ever change it. – irreputable Jan 11 '13 at 06:53

8 Answers8

5

I'd say this is actually language-dependent. If you'll excuse me I'll talk about C# a bit, since I think it'll help answer this question.

I'm not sure if you're familiar with C#, but its design, tools, etc. are very intuitive and programmer-friendly.
One feature of C# (which also exists in Python, D, etc.) that helps this is the property; a property is basically a pair of methods (a getter and/or a setter) which, on the outside, look just like an instance field: you can assign to it and you can read from it just like an instance variable.
Internally, of course, it's a method, and it can do anything.

But C# data types also sometimes have GetXYZ() and SetXYZ() methods, and sometimes they even expose their fields directly... and that begs the question: how do you choose which to do when?

Microsoft has a great guideline for C# properties and when to use getters/setters instead:

Properties should behave as if they are fields; if the method cannot, it should not be changed to a property. Methods are better than properties in the following situations:

  • The method performs a time-consuming operation. The method is perceivably slower than the time that is required to set or get the value of a field.
  • The method performs a conversion. Accessing a field does not return a converted version of the data that it stores.
  • The Get method has an observable side effect. Retrieving the value of a field does not produce any side effects.
  • The order of execution is important. Setting the value of a field does not rely on the occurrence of other operations.
  • Calling the method two times in succession creates different results.
  • The method is static but returns an object that can be changed by the caller. Retrieving the value of a field does not allow the caller to change the data that is stored by the field.
  • The method returns an array.

Notice that the entire goal of these guidelines is to make all properties look like fields externally.

So the only real reasons to use properties instead of fields would be:

  1. You want encapsulation, yada yada.
  2. You need to verify the input.
  3. You need to retrieve the data from (or send the data to) somewhere else.
  4. You need forwards binary (ABI) compatibility. What do I mean? If you sometime, down the road, decide you need to add some sort of verification (for example), then changing a field to a property and recompiling your library will break any other binaries that depends on it. But, at the source-code level, nothing will change (unless you're taking addresses/references, which you probably shouldn't be anyway).

Now let's get back to Java/C++, and immutable data types.

Which of those points apply to our scenario?

  1. Sometimes it doesn't apply, because the whole point of an immutable data structure is to store data, not to have (polymorphic) behavior (say, the String data type).
    What's the point of storing data if you're going to hide it and do nothing with it?
    But sometimes it does apply (e.g. say you have an immutable tree) -- you might not want to expose metadata.
    But then in that case, you would obviously hide the data you don't want to expose, and you wouldn't have been asking this question in the first place! :)
  2. Doesn't apply; there's no input to verify because nothing is changing.
  3. Doesn't apply, otherwise you can't use fields!
  4. May or may not apply.

Now Java and C++ don't have properties, but methods take their place -- and so the advice above still applies, and the rule for languages without properties becomes:

If (1) you don't need ABI compatibility, and (2) your getter would behave just like a field (i.e. it satisfies the requirements in the MSDN documentation above), then you should use a field instead of a getter.

The important point to realize is that none of this is philosophical; all these guides are all based on what the programmer expects. Obviously, the goal at the end of the day is to (1) get the job done, and (2) keep the code readable/maintainable. The guide above has been found to be helpful in making this happen -- and your goal should be to do whatever suits your fancy that will make that happen.

Community
  • 1
  • 1
user541686
  • 205,094
  • 128
  • 528
  • 886
  • Great post, these guidelines and the general rule are *exactly* what I've been trying to get at throughout the comments and original question. One quick question, can properties be changed to methods at a later time while maintaining compatibility? I.e., is the property `x` actually a method `getX()` that I can create at a later date? (And then make `x` a private field, etc.) – Alex DiCarlo Jan 11 '13 at 07:50
  • @dicarlo2: Thanks. :) And well, yes and no... a property `X` is internally its own thing, but when it's compiled, what happens internally is that it *also* generates a getter and setter of the form `get_X` and `set_X`, and the metadata *refers* to those getters and setters. From the caller's perspective, the source code still uses `X`, but the binary uses the getters and setters directly... so I *guess* you could try swapping out the property and just make your own `get_X` method down the road, but I haven't tried it myself. There's a decent chance that it would work but I'm not 100% sure. – user541686 Jan 11 '13 at 07:54
  • 1
    ... but what's for certain is that even though the binary might work, the source code now won't, because the syntax is different. :) – user541686 Jan 11 '13 at 07:56
  • Thanks for sharing the Microsoft guidelines, that's a useful contribution. Things are of course different in a language that has support for properties than one that doesn't. As a contrasting data point, *Effective Java*, 2nd Ed., Item 14 has this to say on the subject: "While it's never a good idea for a public class to expose fields directly, it is less harmful if the fields are immutable. [...] It is less harmful, though still questionable, for public classes to expose immutable fields." – Daniel Pryden Jan 11 '13 at 22:50
  • @DanielPryden: Thanks :) did Effective Java say *why* it's "questionable" to expose immutable fields, or was it just an assertion? – user541686 Jan 11 '13 at 22:53
  • @Mehrdad I added another answer with the quotes related to exposing immutable fields from Effective Java. – Alex DiCarlo Jan 13 '13 at 06:36
4

Encapsulation serves several useful purposes, but the most important one is that of information hiding. By hiding the field as an implementation detail, you protect clients of the object from depending on there actually being a field there. For example, a future version of your object may want to compute or fetch the value lazily, and that can only be done if you can intercept a request to read the field.

That said, there is no reasons for getters to be particularly verbose. In the Java world in particular, even where the "get" prefix is very well entrenched, you'll still find getter methods named after the value itself (that is, a method foo() instead of getFoo()), and that's a fine way to save a few characters. In many other OO languages, you can define a getter and still use syntax that looks like a field access, so there's no extra verbosity at all.

Daniel Pryden
  • 59,486
  • 16
  • 97
  • 135
  • See my response in OP, several other comments mentioned something similar. Lazy initialization is one of the few things I can think of where hiding behind a function benefits the user, however there are some multithreading issues that would need to be carefully checked. Additionally, the function would serve to warn the programmer that either the field is mutable or something is happening under the covers. – Alex DiCarlo Jan 11 '13 at 06:08
  • @dicarlo2: But the whole point of *information hiding* is that the programmer **doesn't** have to know that "something is happening under the covers"! Because the implementation details are hidden, the programmer is free to treat the object as a black box and only interact with it through its public interface, and the implementer of the object is free to change it in any way so long as that public interface can be maintained. This is an important OO concept, and it is a key part of what it takes for a codebase to scale beyond what one person can hold in their head. – Daniel Pryden Jan 11 '13 at 06:13
  • I understand and agree with the definition of *information hiding*. However, I'm arguing from the point of expectation. As the client, I expect that calling `getX()` on a `Point` is not going to do some computation that stalls my program. This fact would be *required* by the interface of `Point` if the field was able to be directly accessed. – Alex DiCarlo Jan 11 '13 at 06:16
  • @dicarlo2: and what if, in a future version of your Point class, you wanted to store X and Y as floating point values instead of integers (or vice versa)? Sure, because this is an immutable object, you could have two sets of fields and initialize them both in the constructor, but that doubles the memory footprint of your object. Expectations is not a strong enough word: the public interface of an object is a *contract* for the behavior it will always support. A method is a weaker contract than a field, and thus provides greater freedom for change without introducing risk. – Daniel Pryden Jan 11 '13 at 06:25
  • @dicarlo2: Also, you need some kind of encapsulation if you want to build your immutable object using a [persistent data structure](http://en.wikipedia.org/wiki/Persistent_data_structure). – Daniel Pryden Jan 11 '13 at 06:32
  • Very good point, in the floating point instead of integers direction, the other, not so much as it would break the contract of the interface. Introducing floating point instead of integers would technically be source code compatible, but very risky due to rounding errors, so I concede on that point. – Alex DiCarlo Jan 11 '13 at 06:36
  • For persistent data structures I'm a bit suspicious, I find it hard to imagine a data structure that I, as the client, could modify without using a method, which is distinctly different from an accessor. – Alex DiCarlo Jan 11 '13 at 06:38
  • @dicarlo2: I don't want to harangue the point, but a simple example of an accessor method that couldn't be replaced with a public field is Java's `String.charAt()`. Not only is the `char[]` array not safe to expose as a field, in Java 6 and below the same array might be shared between instances -- a simple example of a kind of persistent data structure. – Daniel Pryden Jan 11 '13 at 06:47
  • Thanks for the example and the discussion. Out of curiosity, why is char[] not shared between instances in Java 7? (I assume in Java 6 and below it's due to String interning, does this no longer occur?) – Alex DiCarlo Jan 11 '13 at 06:50
  • @dicarlo2: This is getting to be an extended discussion. Maybe you should ask that as a separate question? – Daniel Pryden Jan 11 '13 at 06:51
3

Immutable objects should use direct field access for uniformity and because it allows one to design objects that perform exactly how the client expects they should.

Consider a system where every mutable field was hidden behind accessors while every immutable field was not. Now consider the following code snippet:

class Node {
    private final List<Node> children;

    Node(List<Node> children) {
        this.children = new LinkedList<>(children);
    }

    public List<Node> getChildren() {
        return /* Something here */;
    }
}

Without knowing the exact implementation of Node, as you must do when you design by contract, anywhere you see root.getChildren(), you can only assume one of three things is occurring:

  • Nothing. The field children is returned as is, and you can't modify the list because you will break the immutability of Node. In order to modify the List you must copy it, an O(n) operation.
  • It is copied, for example: return new LinkedList<>(children);. This is an O(n) operation. You can modify this list.
  • An unmodifiable version is returned, for example: return new UnmodifiableList<>(children);. This is an O(1) operation. Again, in order to do modify this List you must copy it, an O(n) operation.

In all cases, modifying the returned list requires an O(n) operation to copy it, while read only access takes anywhere from O(1) or O(n). The important thing to note here is that by following design by contract you cannot know which implementation the library writer chose and thus must assume the worst case, O(n). Hence, O(n) access and O(n) to create your own modifiable copy.

Now consider the following:

class Node {
    public final UnmodifiableList<Node> children;

    Node(List<Node> children) {
        this.children = new UnmodifiableList<>(children);
    }
}

Now, everywhere you see root.children, there is exactly one possibility, namely it is an UnmodifiableList and thus you can assume O(1) access and O(n) for creating a locally mutable copy.

Clearly, one can draw conclusions about the performance characteristics of accessing the field in the latter case, whereas the only conclusion that can be made in the former is that the performance, in the worst case, and thus the case we must assume, is far worse than the direct field access. As a reminder, that means the programmer must take into account a O(n) complexity function on every access.


In summary, with this type of system, wherever one sees a getter the client automatically knows that either the getter corresponds to a mutable field, or the getter performs some sort of operation, whether it be a time consuming O(n) defensive copy operation, lazy initialization, conversion, or otherwise. Whenever the client sees a direct field access, they immediately know the performance characteristics of accessing that field.

By following this style, more information can be inferred by the programmer as to the contract provided by the object he/she is interacting with. This style also promotes uniform immutability because as soon as you change the above snippet's UnmodifiableList to the interface List, the direct field access allows the object to be mutated, thus forcing your object heirarchy to be carefully designed to be immutable from top to bottom.

The good news is, not only do you gain all the benefits of immutability, you are also able to infer the performance characteristics of accessing a field no matter where it is, without looking at the implementation and with confidence that it will never change.

Alex DiCarlo
  • 4,851
  • 18
  • 34
  • This is a well-reasoned argument. My only rebuttal is actually the converse of your conclusion: because callers can infer the performance characteristics, and those characteristics will never change, you have drastically reduced the maintainability of the class. In extremely performance critical code, perhaps that is a reasonable thing to do, but for the vast majority of applications, you need to balance performance against brittleness, and code that is easy to maintain, change, and reuse over time is usually better than code that is perfectly optimal only for what the program does today. – Daniel Pryden Jan 11 '13 at 22:33
  • 1
    @DanielPryden Indeed, it is a tradeoff, however one where I think 99% of programmers just automatically use a getter without thinking about *why* they are using a getter, or if getters are even necessary, simply because it has been the standard since mutability was common. Personally, I'm playing a bit of a devil's advocate here because this is a topic I've mused about from time to time, but never managed to convince myself that the system I described is the *without a doubt* best thing to do, for issues you mentioned above and in our previous discussion. – Alex DiCarlo Jan 12 '13 at 04:08
  • A fundamental weakness in the type system of Java and .NET is that they makes no distinction between a reference which "owns" a mutable object, and a reference which identifies a mutable object owned by something else. The lack of such a distinction lies at the heart of many mutability-related bugs. – supercat Nov 15 '13 at 00:13
2

Joshua Bloch, in Effective Java (2nd Edition) "Item 14: In public classes, use accessor methods, not public fields," has the following to say about exposing immutable fields:

While it’s never a good idea for a public class to expose fields directly, it is less harmful if the fields are immutable. You can’t change the representation of such a class without changing its API, and you can’t take auxiliary actions when a field is read, but you can enforce invariants.

and summarizes the chapter with:

In summary, public classes should never expose mutable fields. It is less harmful, though still questionable, for public classes to expose immutable fields.

Alex DiCarlo
  • 4,851
  • 18
  • 34
  • 1
    So it doesn't say *why* it's "questionable" for public classes to expose immutable fields? – user541686 Jan 13 '13 at 07:39
  • 1
    @Mehrdad The above is verbatim all that is mentioned about immutable fields. I think the implied "questionable" reasons are for all the reasons that hiding the implementation is good, even though many of them do not apply for immutable fields, hence why it's just "questionable" – Alex DiCarlo Jan 14 '13 at 01:21
1

You can have public final fields (to imitate some kind of immutability) but it doesn't mean that referenced objects can't change their state. We still need defensive copy in some cases.

 public class Temp {
    public final List<Integer> list;

    public Temp() {
        this.list = new ArrayList<Integer>();
        this.list.add(42);
    }

   public static void foo() {
      Temp temp = new Temp();
      temp.list = null; // not valid
      temp.list.clear(); //perferctly fine, reference didn't change. 
    }
 }
Andrey Taptunov
  • 9,367
  • 5
  • 31
  • 44
  • 1
    I wouldn't call that an immutable object, however. – Daniel Pryden Jan 11 '13 at 05:55
  • @DanielPryden I would agree. – Alex DiCarlo Jan 11 '13 at 05:58
  • 2
    @DanielPryden: It could be immutable if you don't allow direct field access. So this is a good example to demonstrate the purpose of encapsulation behind getters here. Make the field private and you have an immutable list {42}. – Thilo Jan 11 '13 at 05:58
  • @Thilo: make the field private, and remove the mutator method, you mean? But yes, I agree. Your comment is clearer than the original answer, though. – Daniel Pryden Jan 11 '13 at 06:03
1

What are the reasons for the continued use of accessors versus direct field access of immutable objects? Specifically, are there advantages to forcing the user to use accessors (for the client or library writer), and if so what are they?

You sounds like a procedural programmer asking why you cannot access fields directly, but have to create accessors. Main problem is that even the way you put your question is wrong. This is not how OO design works - you design object behavior through it's methods and expose that. Then you create internal fields if necessary which you need to implement that behavior. So putting it this way: "I am creating that fields and then expose each by a getter, this is verbose" is a clear sign of improper OO design.

Slava
  • 43,454
  • 1
  • 47
  • 90
  • Do you mean, not being verbose is a sign of improper OO design? – Nilesh Apr 13 '18 at 07:23
  • @Nilesh No, I mean defining fields first and mechanically making getters for each of them is that sign, verbosity of that is just a consequence. – Slava Apr 13 '18 at 12:49
0

It's a OOP practice to encapsulate fields and then expose it only through getters method. If you expose field directly this means that you will have to make it public. Making fields public is not good idea as it exposes inner state of object.

So making your field/data members public is not a good practice and it violates Encapsulation principle of OOP. Also i would say it's not specific to Immutable objects; this is true for non-immutable objects as well.

Edit As pointed by @Thilo ; Another reason : Maybe you don't need to expose how a field is stored.

thanks @Thilo.

rai.skumar
  • 10,309
  • 6
  • 39
  • 55
  • Right, this is exactly what I am asking, why? What are we encapsulating by forcing the client to use a getter on an immutable field? – Alex DiCarlo Jan 11 '13 at 05:47
  • Still, your edit does not answer the question, *what exactly are we gaining by encapsulating an immutable field*. – Alex DiCarlo Jan 11 '13 at 05:50
  • we encapsulate data or state of the object or in other words we hide the state. – rai.skumar Jan 11 '13 at 05:51
  • 3
    +1 even with immutable objects, there is still something to be said for encapsulation to hide implementation details. Maybe you don't need to expose how a field is stored. – Thilo Jan 11 '13 at 05:53
  • we provide getter and setter to provide restriction over the field change. – Stardust Jan 11 '13 at 05:55
  • There is no state in an immutable object. Thilo's response is good, why not add it as an answer rather than a comment? – Alex DiCarlo Jan 11 '13 at 05:55
0

One very practical reason for the continued practice of generating (I hope nobody writes them by hand nowadays) getters in Java programs, even for immutable "value" objects where, in my opinion, it is unnecessary overhead :

Many libraries and tools rely on the old JavaBeans conventions (or at least the getters and setters part of it).

These tools, that use reflection or other dynamic techniques to access field values via getters, cannot handle accessing simple public fields. JSP is an example that comes to my mind.

Also modern IDEs make it trivial to generate getters for one or many fields at a time, and also to change the name of the getter when the name of the field is changed.

So we just keep writing getters even for immutable objects.

Pierre Henry
  • 16,658
  • 22
  • 85
  • 105