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...

Cory Kendall
  • 692
  • 1
  • 5
  • 10
  • 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. – Tomáš Zato Mar 09 '15 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;`. – Cory Kendall Mar 09 '15 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. – Tomáš Zato Mar 09 '15 at 16:53
  • 4
    If the underlying values were mutable, the private+accessors road wouldn't prevent it either —`myObj.getFoo().setFrob(...)`. – Beni Cherniavsky-Paskin Mar 25 '15 at 10:54

5 Answers5

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.

Brandon
  • 4,555
  • 19
  • 21
  • 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 :). – Cory Kendall Mar 18 '13 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. – Clockwork-Muse May 28 '13 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. – kritzikratzi Nov 05 '14 at 18:21
  • It would be better to avoid arrays entirely and use collections. Return `Collections.unmodifiableList(obj)` which returns an immutable view. –  Nov 05 '14 at 18:23
  • I don't agree that arrays should be avoided in favor of collections. Arrays have their place. Collections have their place. – Brandon Nov 05 '14 at 19:44
  • 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. – Clockwork-Muse Nov 05 '14 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.". – Deduplicator Oct 30 '15 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.
kritzikratzi
  • 549
  • 4
  • 5
  • 8
    Nice to see some sanity once in awhile :) – Navin May 09 '16 at 02: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 Aug 31 '16 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](https://www.yegor256.com/2014/09/16/getters-and-setters-are-evil.html) – Ray Toal Jan 11 '20 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 Oct 21 '20 at 06:29
  • If there was a super-upvote button I'd smash it. – user16217248 Jun 10 '23 at 04: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.
Tulains Córdova
  • 39,201
  • 12
  • 97
  • 154
  • 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 May 28 '13 at 15:29
  • 2
    @KChaloux No. Version 1 is not as exposed. If you refactor it and change the name of foo to foo2, the outside world wouldn't notice. In version 1 you promise not to break the contract. In version 2 you promise not to refactor. – Tulains Córdova May 28 '13 at 16:17
  • 2
    Sure, but this only applies in a case where the code changes. As it is, shown, the level of encapsulation is the same. I'm not arguing against the fact that it's brittle and vulnerable to refactoring. I just don't believe we can equate "would break encapsulation if" with "does break encapsulation". – KChaloux May 28 '13 at 16:49
  • 2
    @KChaloux Encapsulation is already broken even if code compiles and runs, then if code is changed you break the contract and you break the client code. Also: Do you really think code beeing changed is a rare event? "this only applies in a case where the code changes"... then it applies all the time. I give you this painful truth: **code changes**. There's even a whole discipline that is based on the fact that code changes: version control. – Tulains Córdova May 28 '13 at 17:34
  • 1
    That's like saying "I'm dead, because I will inevitably die". Even if something is guaranteed to happen in the future, it doesn't mean it has already happened. I suppose I'm just arguing semantics at this point, and I'm certainly not arguing in favor of using the brittle, *likely-to-break-encapsulation-in-the-future* model. I just don't agree that you can accurately say something is broken because it will, at some point in the future, break. – KChaloux May 28 '13 at 18:30
  • 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. – Tulains Córdova May 28 '13 at 18:38
  • 2
    I'm getting a warning that we're going into too-long-discussion territory, so I'll stop it here, but I don't agree. Perhaps this is because I'm spoiled by languages that offer proper properties, or languages like Scala that support a more functional paradigm, where it's common to expose immutable values, given the guarantee that they are in fact immutable. – KChaloux May 28 '13 at 18:42
  • 1
    @KChaloux You confuse immutability with "this code will never be changed". One thing has nothing to do with the other. **You can have an immutable class that has been refactored hundreds of times**. No relation at all. One thing happens in run time, the other in design/programming time. In object-oriented and functional programming, an immutable object is an object whose state cannot be modified after it is created. It has nothing to do with the source code not beeing ever rafactored. – Tulains Córdova May 28 '13 at 18:45
  • 2
    You're talking about changing the behavior of the member entirely (from an immutable `static final` to a mutable variable). In this case, yes, all bets are off, and it should go in a getter. None of this has a bearing on whether or not encapsulation is broken **before this happens**. – KChaloux May 28 '13 at 18:53
  • 1
    @KChaloux If you change the name or type of a member, client code will break because client code knows too much of the inner workings of your class. That's preciselly why encapsulation exists, in order to client code not having to know anything about the innards of a class, preventing this way the coupling of the client code to the the inner functioning of a class. All that was invented because code always gets changed. – Tulains Córdova May 28 '13 at 19:00
  • 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 May 28 '13 at 20:41
  • 1
    @JAB Yes, that's called dependency inversion principle, the "D" in SOLID. "Depend on abstrations, not on details". The interface ( method names ) are an abstraction whereas internal functioning of classes are details. – Tulains Córdova May 28 '13 at 20:53
  • 1
    In other words, it's better to avoid explicit getters and setters in the first place unless the use case is trivial and is guaranteed to continue being trivial. – JAB May 28 '13 at 21:12
  • 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? – Thomas Jung Nov 12 '13 at 19:35
  • @ThomasJung You should not rename the public getters and setters if there's already code using your API. That will break client code. – Tulains Córdova Nov 14 '13 at 12:48
  • 1
    That's my point: You don't rename them. So you wouldn't refactor private field names either given the convention. That being so, I have observed immutability expectations being relaxed as code design progresses, which resulted in the obvious necessity to use accessors anyway. While I maintain that using `public final` for a **guaranteed immutable** field does not break encapsulation (part of the public contract), in reality _that guarantee can rarely be made_. Using them may prevent proper evolution of the API if they need to be mutable in the future, which I presume is your point, right? – Thomas Jung Dec 05 '13 at 19:58
  • 1
    @ThomasJung My point is that components should be black boxes. The less a component knows about the innards of other components, the less coupling there is, and the easier the maintenance will be. – Tulains Córdova Dec 05 '13 at 20:23
  • 1
    Two of these three points are silly or wrong, so I'm downvoting. – user949300 Nov 05 '14 at 19:00
  • 3
    @user949300 Tell me which ones so I can learn. – Tulains Córdova Nov 05 '14 at 19:04
  • 2
    @ThomasJung Private field names are an internal implementation detail and shouldn't be exposed (as a general rule; there may be exceptions). It's not necessary (except for the godawful Java bean convention) to have a getter/setter for *every* field. One of the points of OOD is to avoid locking clients to your internals. Unfortunately a lot of common Java practices do not follow OOD principles. – Andres F. Nov 05 '14 at 21:20
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.

ipaul
  • 484
  • 2
  • 6
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.

TommiZi
  • 11