In this mini-Fragment episode, Donn talks about Item #14 of the Effective Java series – In public classes, use accessor methods, not public fields. You’ll learn why it’s important to use accessors in your public classes and some caveats to this rule.
For example, you may decide that the class is a private inner class or package private. Donn digs into the details in this week’s episode. This is a glimpse of what’s to come in Item #15, which is coming soon…
Show Notes
Sponsor
Contact
- @fragmentedcast [twitter.com]
- @donnfelker [twitter.com]
- @kaushikgopal [twitter.com]
Transcript
Donn Felker: Today, I’m going to talk about Item 14 in Effective Java, by Joshua Bloch.
For those of you who are just joining us, we do a small “mini-Fragment” episode every once in a while. Some of these episodes are based around Effective Java, a book that’s packed full of great information for every single Java developer. It gives a bunch of patterns and practices that you should implement in order to be a good Java developer. Our goal here is to relate them to Android development, and to talk about each of the items as we come across them.
Today, I’m talking about Item 14, which states: “In public classes, use accessor methods, not public fields.” I think the key phrase here is “In public classes…” Keep that in mind: Joshua is not talking about private or package-private classes, but about public classes only.
Some folks are tempted to create data objects that, instead of having accessors and mutators (getters and setters, respectively), just make their fields public. They’ll make a public value, and they’ll just access it that way. One of the reasons that this is looked upon badly by some of the hard-line, object-oriented programmers is because it doesn’t offer the benefits of encapsulation:
You can’t change the representation without changing the API, you can’t enforce invariants, and you can’t take auxiliary action when a field is accessed. Effective Java, page 71
There are a bunch of problems which you can encounter when you expose these values as public fields.
On a personal note, I agree with him that when it comes to public classes, which are accessible outside of their package, you should provide some type of getter or accessor method. This allows you to preserve some flexibility around that class’s internal representation.
If (for some reason) your class does expose a public field, all hope of ever changing its representation is basically lost. That public class—again, a public field or method—is a part of the API that you’re giving. If you build a library and make something public, it’s going to be used by people who are using your library. You need to think hard and long about whether something should be public. If it’s a field, you’re kind of tied to it. If you realize later (for some reason), “Ooh, I need to change that because of these problems,” there will be all sorts of folks who are using that public field.
Instead, you should probably think about encapsulating the field in some type of accessor (i.e. a getter method), and that’s exactly what item 14 is saying.
Effective Java then dives in, saying that the main reason for this item (again) is to preserve the class’s internal representation of the data and give you some flexibility in how it works (if you need to change it).
Now, there is a flip side to this coin (which everyone probably has on the tip of their tongue): if the class is package-private or within a private nested class, there’s nothing inherently wrong about exposing its data fields, assuming that they do a good job of describing any type of data or abstraction provided by said class. The reason for this is that it generates less visual clutter (i.e. you have less code lying around) than the typical accessor method approach. I’m sure you’ve seen this: if you have ten fields in your class, and then (for some reason) you generate getters and setters using Android Studio or IntelliJ, your class suddenly explodes from 15 lines of code to 30-50 lines of code, and you have a larger set of code to maintain. So, if the class is private or package-private, you can go ahead and set your fields to public.
One of the reasons why it’s okay to do this in a private or package-private class is because the client code (your library, or whatever) is tied to the class’s internal representation and the package containing that class. If changing something in the class is desirable, you can make that change without having to worry about what it will do to the public API. You aren’t changing a method name, its signature, or anything like that which could be a breaking change.
However, you still have to be careful about what you’re doing to the data, since you could be changing what’s happening (against the users expectations). If (for some reason) you’re changing a value when another method is called, you need to (of course) notate that in your release notes.
Please note that you’ve probably seen different classes in Java itself
that completely violate this advice—for instance, the Point
class and
the Dimension
class. Apparently, according to the book, some internals
of the Dimension
class which are exposed create a serious performance
problem that’s still in Java today. I haven’t dug into this exact issue,
so this is just from Effective Java, but Joshua is explaining, “Look,
Java has actually suffered from this, and they’re realizing it too.”
He’s giving the advice to provide a public accessor method, which helps
you fix or alleviate some of the problems that existing Java classes
have run into.
There’s also another caveat to this (which a lot of the folks out who are really into immutable data types are probably screaming at me right now): while it’s never a good idea for a public class to expose fields directly, it’s usually less harmful if those fields are immutable. You can’t change the representation of that data because it’s immutable.
Suppose I have a class called Time
with two variables: Hour
and
Minute
. We could set those as public final int Hour
and
public final int Minute
. We could then pass those values in when we
create an instance of the Time
class. So you would say new Time
,
pass in the hour and the minute, and those public fields would then be
instantiated. There are no setter methods. These are public final
fields, so you cannot change them. In that case, the class guarantees
that each instance represents a valid time, and something will not be
changed.
In summary, item 14 states that public classes should never expose mutable fields. Of course, there are some caveats to that, such as inner private and package-private classes, and also if those fields are not going to be mutable.
I really hope that this helps you. Please pay attention to the next item (item 15), where we’re going to be talking about minimizing mutability. This is a huge topic in Android right now, and we’ll talk about these in the next Fragment.