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
Four disadvantages that I can think of:
- 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.
- 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.
- 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.
- 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
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:
- 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.
- 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
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
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
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.