Many developers are familiar with getters and setters in Java.
Let’s find out why that is and what the unfortunate consequences are of including them in your code.
Programmers are prone to putting in a lot of getters and setters in Java into their classes. This practice is so common that few people ever question the reasons for it. I’ve recently realized that it is better not to do this, so I’ve started avoiding it in my Java code. This blog post will discuss the reasons. But first, a quick history lesson.
Getters and setters in Java originated in the JavaBeans specification, which came out originally in late 1996 and was updated to version 1.01 in August 1997. It was originally intended to create objects that could then be used as building blocks for applications. The idea was that a user could use a builder tool to connect and customize JavaBeans components to create an application. A button in an AWT app would be a bean. (AWT was the predecessor to Java UI library Swing). Some JavaBeans could be more similar to regular applications. These may be combined into complex documents. For example, a spreadsheet bean might be embedded within a webpage.
JavaBeans are objects that adhere to these conventions.
- It must be able to construct zero arguments and cannot fail.
- It can be accessed and modified via the ‘getter’ and ‘setters’ methods.
- The accessor method must be named getFoo for any property of a bean named Foo. For boolean properties the getter can alternatively be
- The setter method for Foo must be called
- Beans are not required to present a getter or setter for each property. Property with no getter is read-only, while property with setter but no getter is written-only.
Although the specification lists many use cases, it is clear that JavaBeans were designed as objects with behavior and not just data bags. Although JavaBeans are now largely forgotten, it is clear that JavaBeans were conceived as objects with behavior. However, getters and setters in Java methods have not been lost.
The Metaphor is Wrong
Although it seems evident that “get” and “set” are synonymous, is this correct? JavaBeans uses “get” for a query. This is an operation that has no side effects. However, in real life, getting is an action that alters the state. If I take a book off the shelf, it is gone. This may seem like pedantry. However, I believe this misconception encourages us all to misunderstand how objects interact with one another. If we had a Thermometer Class, most Java developers would write code to measure the temperature like this:
Is it the job of a thermometer to “get” the temperature? It is not! No! This is why I am so laborious. This is because “get” can be a command to the thermometer. We don’t want to tell the thermometer to do something here. It is already doing its job (measuring temperature), and we only want to see its current reading. We do the actual reading. This makes it easier to read the code:
This puts the responsibility where it belongs. However, please consider whether an accessor is necessary, Because…
Objects are not data structures
Writing classes with getters and setters in Java can have subtle effects on how we code. This naturalizes the idea of reaching into objects to obtain the data we need, processing it, and then updating the objects with the results. Rather than getting the objects to do the processing, we should reach into the objects. It encourages us to think of objects as a collection of data. The setters update the getters with the data we have retrieved through the getters. The code that runs on the data is located elsewhere.
ORM frameworks can be used to enforce this if our programming habits lead us to treat objects as data structures. Even worse, Spring framework (and you probably are a Java developer) defaults to creating singletons for all beans. This isn’t very clear because Spring beans are not compatible with JavaBeans. Now you have a system that is composed of singleton objects and operates on data structures without behavior. It’s not hard to imagine procedural programming, which is keeping code and data separated.
This is an excellent thing. Java is an object-oriented programming language. One of the greatest strengths of OO is the ability to create classes of objects whose names or interactions correspond to the problem domain. This allows us to write code that is oriented towards solving the problem. It does not obscure the fundamental programming constructs or primitive data types. It allows us to see the forest through the trees. This should not be lost.
What can you do instead?
Stop using getters and setters in Java wherever possible. It may be necessary, but stop using the IDE’s ability to generate getters and setters in Java for you. This is a quick way to do the wrong thing. Name an attribute that you want to be exposed to an object. But, make sure to examine why it is necessary. Ask yourself why you are doing this. Is the task possible to delegate to the object? Let’s say, for example, that I have a class that represents a currency amount, and I want to sum several transactions:
Instead of using the
getValue accessor, why not give Amount class an
add() method to do the summing?
This has many benefits. Perhaps you were apprehensive about the idea of using a pair to represent a currency amount. BigDecimal is a better choice. This is easier because the internal representation of the second example is more tightly encapsulated. It only needs to be changed in one location.
Perhaps you need to access an object’s data to determine if it is equal to another. In this case, consider implementing an
equals() method on the object and have it test equality for you. Mockito can be used to create spy agents. This eliminates the need for argument captors. Instead, you can create an object with equal value and pass it to the verify statement to compare.
You will need to create accessors at times. You may have to access primitive representations to your data, for example, to persist it in a database. Is it essential to use the get/set name convention? If you answered, “yes, that’s how it works in Java,” I encourage you to read the JavaBeans specification. Is your JavaBean written in the manner that the specification requires? Do you expect your objects to conform to the standard?
You will need to create mutators less often. Functional programming is the hottest trend in the industry right now. The principle of immutable information is an excellent one. This principle should also be applied to OO applications. It is not necessary to modify the state. It would be best if you considered it essential, not. Don’t add a Mutator Method. If you write code that results is a new state, return new instances to reflect the new state whenever possible.
The arithmetic methods used to calculate a BigDecimal instance’s value do not alter it; they return new BigDecimal instances that reflect their results. Programming is possible today because we have sufficient memory and processing power. Spring framework doesn’t require dependency injection setter methods, but it can inject via a constructor argument. This approach is indeed the way the Spring documentation recommends.
Some technologies require that classes follow the JavaBeans convention. If you are still creating JSP pages for your view layers, EL or JSTL, expect that response model objects have getter methods. Libraries for serializing/deserializing objects to and from XML may require it. ORM frameworks may need it. ORM frameworks may require these data structures. I recommend that you hide them behind architectural boundaries. These data structures should not be misinterpreted as objects and allowed to leak into your domain.
Programmers who are proficient in other languages often criticize Java. Programmers who work in other languages often blame Java. They might say things such as “it’s too wordy” or “there is too much boilerplate.” Java has its faults. But, when I dig deeper, I find that these criticisms are directed at specific practices and not the language itself. Bad practices aren’t fixed in stone. They evolve over time. The indiscriminate usage of getters and setters in Java is a bad habit that we can stop.