59

I see most immutable POJOs written like this:

public class MyObject {
    private final String foo;
    private final int bar;

    public MyObject(String foo, int bar) {
        this.foo = foo;
        this.bar = bar;
    }

    public String getFoo() {
        return foo;
    }

    public int getBar() {
        return bar;
    }
}

Yet I tend to write them like this:

public class MyObject {
    public final String foo;
    public final int bar;

    public MyObject(String foo, int bar) {
        this.foo = foo;
        this.bar = bar;
    }
}

Note the references are final, so the Object is still immutable. It lets me write less code and allows shorter (by 5 chars: the get and ()) access.

The only disadvantage I can see is if you want to change the implementation of getFoo() down the road to do something crazy, you can't. But realistically, this never happens because the Object is immutable; you can verify during instantiation, create immutable defensive copies during instantiation (see Guava's ImmutableList for example), and get the foo or bar objects ready for the get call.

Are there any disadvantages I'm missing?

EDIT

I suppose another disadvantage I'm missing is serialization libraries using reflection over methods starting with get or is, but that's a pretty terrible practice...

4
  • What the hell? final doesn't make a variable the object immutable. I normally use a design where I define final fields before creating callback so that callback can access those fields. It can, of course call all methods including any setX methods. Commented Mar 9, 2015 at 10:02
  • @TomášZato There are no setX methods on String, int, or MyObject. In the first version, final is only to ensure that methods other than the constructor within the class don't try to bar = 7;, for example. In the second version, final is necessary to prevent consumers from doing: MyObject x = new MyObject("hi", 5); x.bar = 7;. Commented Mar 9, 2015 at 16:41
  • 1
    Well your "Note the references are final, so the Object is still immutable." is misleading - that way it appears you think any final Object is immutable, which it isn't. Sorry for the misunderstanding. Commented Mar 9, 2015 at 16:53
  • 4
    If the underlying values were mutable, the private+accessors road wouldn't prevent it either —myObj.getFoo().setFrob(...). Commented Mar 25, 2015 at 10:54

5 Answers 5

41

Four disadvantages that I can think of:

  1. If you want to have a read-only and mutable form of the same entity, a common pattern is to have an immutable class Entity that exposes only accessors with protected member variables, then create a MutableEntity which extends it and adds setters. Your version prevents it.
  2. The use of getters and setters adheres to the JavaBeans convention. If you want to use your class as a bean in property-based technologies, like JSTL or EL, you need to expose public getters.
  3. If you ever want to change the implementation to derive the values or look them up in the database, you'd have to refactor client code. An accessor/mutator approach allows you to only change the implementation.
  4. Least astonishment - when I see public instance variables, I immediately look for who may be mutating it and worry that I am opening pandora's box because encapsulation is lost. http://en.wikipedia.org/wiki/Principle_of_least_astonishment

That said, your version is definitely more concise. If this were a specialized class that is only used within a specific package (maybe package scope is a good idea here), then I may consider this for one-offs. But I would not expose major API's like this.

7
  • 4
    Great answers; I have disagreements with some of them, but I'm not sure this site is the best forum for discussion. In short, 1) I can break others by using the mutable form where they assume it's immutable (perhaps I should add final to the class in my examples), 2) I agree, 3) I would argue it's no longer a POJO in that case, 4) I agree for now, but hopefully discussions like this can change what is least astonishing :). Commented Mar 18, 2013 at 20:32
  • 2
    5) You can't safely expose inherently mutable classes/types - like arrays. The safe way is to return a copy from a getter each time. Commented May 28, 2013 at 16:19
  • 1
    @Clockwork-Muse you can, if you assume people aren't idiots. When accessing someObj.thisList.add() it's obvious that you're modifying some other objects state. With someObj.getThisList().add() it's unknown. You need to ask the documentation or look up the source, but from the method declaration alone it's impossible to tell. Commented Nov 5, 2014 at 18:21
  • 2
    @kritzikratzi - The problem is that you can't guarantee that your code won't run into idiots. At some point it will (which might even end up being oneself!), and the surprise could be a huge problem. Commented Nov 5, 2014 at 21:26
  • 2
    Well, letting any mutable type inherit from an immutable type is inherently wrong. Remember that an immutable type gives the guarantee "I don't change. Ever.". Commented Oct 30, 2015 at 16:05
35

Get rid of the getters/setters too, and you're fine!

This is a highly controversial topic amongst Java programmers.

Anyways, there's two situtation where i use public variables instead (!) of getters/setters:

  1. public final To me this signals "I'm immutable" much better than just a getter. Most IDEs will indicate the final modifier with a 'F' during auto-completion. Unlike with getters/setters, where you have to search for the absence of a setXXX.
  2. public non-final I love this for data classes. I just expose all the fields publicly. No getters, setters, constructors. No nothing. Less than a pojo. To me this immediately signals "look, i'm dumb. i hold data, that's all. it's YOUR job to put the right data inside of me". Gson/JAXB/etc. handle these classes just fine. They're a bliss to write. There's no doubt about their purpose or capabilities. And most importantly: You know there are no side effects when you change a variable. IMHO this results in very concise data models with few ambiguities, whereas getters and setters have this huge problem where sometimes magic happens inside of them.
5
  • 8
    Nice to see some sanity once in awhile :)
    – Navin
    Commented May 9, 2016 at 2:34
  • 3
    Until the day comes where your model evolves and one of the old fields can now be derived from another. Since you would like to maintain backwards compatibility the old field must remain but instead of just changing the getter and setter code you would have to change everywhere else where this old field was used to ensure that it follows the new model representation. Easy to write.. yeah sure... nightmare to maintain in the long run... That is why we have getter/setter or better still in C# property accessors.
    – Newtopian
    Commented Aug 31, 2016 at 17:57
  • 1
    This is a great answer (although it doesn't answer the direct "have I overlooked any other disadvantages") because it alludes to the reality that we have "active" classes that do things and "dumb" data structures. Java has just one construct, the class, to represent both! Unfortunate! So, we programmers have to adapt: dumb data structures get everything public non-final. But in the former case, for our active classes, the fields should have neither getters nor setters, as they are evil
    – Ray Toal
    Commented Jan 11, 2020 at 19:01
  • "And most importantly: You know there are no side effects when you change a variable.": But changing a variable is a side effect.
    – Giorgio
    Commented Oct 21, 2020 at 6:29
  • If there was a super-upvote button I'd smash it.
    – CPlus
    Commented Jun 10, 2023 at 4:06
8

In layman's words:

  • You violate encapsulation in order to save a few lines of code. That defeats the purpose of OOD.
  • Client code will be hard coupled to the names of your class members. Coupling is bad. The whole purpose of OOD is preventing coupling.
  • You are also very sure your class will never need to be mutable. Things change. Change is the only thing that is constant.
20
  • 7
    How is this a violation of encapsulation? Both versions are as exposed to the outside world as each-other (with read-only access). You're correct about the hard coupling and inflexibility of change, though, I won't argue that.
    – KChaloux
    Commented May 28, 2013 at 15:29
  • 4
    @KChaloux You confuse "encapsulation violation" with "code failing to compile and having to waste time fixing it", one happens first. The second happens later. To make it clear: encapsulation is already violated in the present. The innards of your class are no longer encapsulated in the present time. But client code will break in the future if class changes in the future, because encapsulation was broken in the past. There are two different "breaks", encapsulation today, client code tomorrow, because of lack of encapsulation. Commented May 28, 2013 at 18:38
  • 11
    Technically, when you use getters/etc. the client code will be hard coupled to method names instead. Look at how many libraries have to include old versions of methods with a "deprecated" tag/annotation because older code is still reliant on them (and some people may still use them because they find them easier to work with, but you aren't supposed to do that).
    – JAB
    Commented May 28, 2013 at 20:41
  • 6
    Pragmatic: How often do programmers actually refactor private field names without renaming the public getters/setters? From what I can tell, there's a community-wide convention in place to name them the same, even if that convention may only a result of IDE tools that generate getters and setters from field names. Theoretical: From an object-oriented-modelling perspective, however, anything public is part of the public contract, and not an internal implementation detail. So if someone decides to make a final field public, aren't they saying the this is the contract they promise to fulfil? Commented Nov 12, 2013 at 19:35
  • 3
    @user949300 Tell me which ones so I can learn. Commented Nov 5, 2014 at 19:04
6

One possible disadvantage I can see offhand is that you're tied to the internal representation of the data in the class. This probably isn't a huge deal, but if you use the setters, and you decide that foo and bar will be returned from some other class defined somewhere else, the classes consuming MyObject would not be required to change. You would only need to touch MyObject. However, if you use the naked values, then you would have to touch everywhere your MyObject i used.

0
0

If you designed a class to be immutable and then you find it needs to be mutable you have made fundamental mistakes when designing your model. I believe that here is best objects of one type are converted into another to better capture business in code. For example, UnassignedJob (has no assignee) -> AssignedJob (has assignee), instead creating a generic Job type and then mutating its assignee field. Job::setAssignee moving business logic into what's meant to be a DTO (POJO).

Getters and setters are not APIs, POJOs should never mutate! Mutability should live along side the business rules that demand it not buried in a DTO.

Member names change and so should the getters, ergo, the argument about client code being coupled etc is thrown out of the window. IDEs help with refactoring nowadays.

As for the OOD encapsulation utopia, this is laughable at best, especially, when people start writing dumb setters which will accept anything under the sun.

Not the answer you're looking for? Browse other questions tagged or ask your own question.