Beliebte Suchanfragen
//

Why do you write accessor methods?

23.2.2016 | 11 minutes of reading time

Erik PetzoldRaimar Falke .

We all know the getXxx() and setXxx() methods in languages like Java. They appear in almost every project and everybody uses them. But why do we cling to these methods? Do we need them? What about just making the fields public? If you are also asking yourself these questions please read on.

Object Inspection

Where are these methods coming from? For different purposes there was the need to inspect objects at runtime. The JDK 1.1 therefore brought the Reflection API, which allows to examine and manipulate objects. This new API is used by the JavaBean Specification , which amongst other things defines a standard how to use reflection to access properties of objects.

JavaBeans are often mentioned in discussions about Java objects that simply hold values. Usually it is said that a Java object has to meet three conditions to be considered a JavaBean:

  • a constructor without any arguments (aka default constructor)
  • private attributes with their accessor methods (getters and setters)
  • implements the java.io.Serializable interface

The original specification is much more than these three lines — it’s about 114 pages — and it has a completely different focus:

The goal of the JavaBeans APIs is to define a software component model for Java, so that third-party ISVs [Independent Software Vendors] can create and ship Java components that can be composed together into applications by end users.

A Java Bean is a reusable software component that can be manipulated visually in a builder tool.

When reading these two quotes (or even better the whole spec), one can see, that the document is about composable components (often graphical), that have properties and behaviour. The properties of these components should be editable in a graphical tool (builder), which leads to the requirements listed above. The building tool can then use introspection as defined in the JavaBean specification to manipulate the properties and serialization to store the beans. But the spec is much more than that, JavaBeans should support many more features, e.g. events. Properties of JavaBeans can be much more complex than just storing and returning a value of a private field. The value can be computed, there are bound properties which perform notifications on changes and there can even be constrained properties, whose value changes can be rejected through vetoes.

In conclusion we can see that JavaBeans is a specification for (visual) application components with behaviour. So it is surprising that it is regularly mentioned in the context of data objects without behaviour. So we have to ask: is the spec really the best fit for such objects?

The reason behind the widespread use of the bean specification might be, that the standardized way of accessing properties (originally intended for builder tools) is also a basic requirement for other tools like mappers and marshallers, which work with the data part of objects.

So nowadays, getters and setters are everywhere, especially when objects are processed by standard frameworks or libraries. The accessor methods do not even have to be hand-written, but can be generated quite easily by the IDE or even more convenient at compile-time by tools like Project Lombok , Joda Beans and the case objects of Scala. But on the other side this creates the risk, that programmers don’t think about these methods and simply generate all possible accessors without asking if they are indeed needed. And these methods may be harmful, as they can break basic principles of object-orientation.

The Object Oriented Perspective

The basic idea of object-oriented programming is to have objects, which combine state and behaviour. The state is owned by the object and therefore internal and encapsulated. Encapsulation means that the structure and details of the internal data are not part of the public interface.

On the other side are data objects like entities and value objects (as defined in Domain Driven Design) or data transfer objects (DTO s), which typically have private instance variables and public getter/setter methods. These methods commonly mirror the internal structure and directly access all internal variables without further logic. This leads to two issues (see also ) :

  1. These objects do not follow the general idea of object orientation, as there is no data hiding within these objects.
  2. As a consequence, developers tend to create anemic domain models, which do not provide domain specific operations, but only hold data. A consequence is that the interaction with the “data holding objects” is through getter and setter invocations.

For example an address change of a customer should be performed by calling customer.changeAddress(...) instead of customer.setAddress(...). The difference is that changeAddress() can perform other actions e.g. change verification flags or send notifications. Unfortunately the existence of the accessor methods allows the developer to easily solve his problem (change the address) without being required to think about proper method names and semantics. You are not alone in this: we see the widespread usage of accessor methods in all projects. Because our mind is shaped to use accessor methods it requires a lot of effort to avoid this pattern.

We have already mentioned that frameworks and libraries may require to use setter and getter methods. But how often is it really required by the 3rd-party software?

Frameworks as a Cause?

Usually in each project different frameworks are used to handle objects. So there is for example Jackson to process JSON and Hibernate as an ORM (Object-Relational Mapper). Because we find these two frameworks in almost every Java project we want to take a closer look whether these frameworks do need accessor methods and how the JavaBeans Specification is related to this. A simple data model has been chosen to examine these questions:

A company has an address and an unsorted set of customers. Each customer also has an address. An address consists of a street, house number, zip code and city. All the attributes of an address are strings.

Let’s consider three variant: private fields with and without accessor methods and public fields. We examine both Jackson (version 2.7.0) and Hibernate (version 5.0.7) in our tests.

We start with private fields. Jackson and Hibernate work fine with accessor methods. That is the standard way (jackson-normal , hibernate-normal ). Without these methods, Jackson requires a configuration statement to set and access the fields (jackson-normal-without-accessors ):

1objectMapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);

Hibernate also supports objects without accessor methods:

Although not required, it is recommended to follow JavaBean conventions by defining getters and setters for you entities persistent attributes. Hibernate can also directly access the entity’s fields.

Attributes (whether fields or getters/setters) need not be declared public. Hibernate can deal with attributes declared with public, protected, package or private visibility.

After verification we can confirm that Hibernate works without additional adjustments. Such objects (private fields and no accessor methods) are only useful if you have domain methods which work with the private fields. These methods of course may also return some of the fields directly or a computation based on the private fields (like a view). In this regard such objects match closely the ideal we described in the object-oriented section above.

Making your fields public is unconventional but not a problem for Jackson and Hibernate (jackson-public-fields , hibernate-public-fields ). In such a case the accessor methods are rendered superfluous and the source code can shrink. Please note that in the JPA specification  public fields are forbidden (from section 2.2): “The instance variables of a class must be private, protected, or package visibility independent of whether field access or property access is used.“ So it turns out that the behaviour we observe with Hibernate contradicts the JPA specification by supporting public fields. This deviation from the specification is also the reason why the Eclipse IDE shows errors for public fields: “The Java field for attribute ‘name’ is public/final”. These errors can be disabled by changing the JPA settings under: project settings / JPA / Errors/Warnings / Attribute / The java field for attribute is final/public – error to warning, info or ignore.

As a last experiment we made the fields final in addition to public to reflect the intention of value objects. The fields are initialized in the constructor which therefore gets all values as parameters. Jackson supports this but requires an annotation for the constructor (jackson-final-public-fields ):

1@JsonCreator
2  public Customer(@JsonProperty("firstName") String firstName,
3                  @JsonProperty("lastName") String lastName,
4                  @JsonProperty("customerAddress") Address customerAddress) {
56  }

Java 8 supports the discovery of parameter names using reflection. Jackson can use such data and with an additional maven dependency the annotation shown above is unnecessary (jackson-final-public-fields-no-annotations ).

Hibernate is used mostly for storing and retrieving entities. These types of object do not benefit from final fields and therefore we did not test Hibernate with public final fields.

The following table shows how the two frameworks deal with different field visibilities and/or the existence of accessor methods.

FrameworkJacksonHibernate
Field VisibilityAccessor Methods
Privateexistent ✓
non-existent ✓✓*
Publicnon-existent ✓✓**

* Hibernate supports this but the scenario is only useful if domain methods are present.
** Hibernate supports this but internal structure of entity is disclosed.

We found that the Jackson and Hibernate framework do not require accessor methods. So you can choose to either make the fields public (and maybe even final) to allow easy interaction with value objects. Or you can make the fields private for entities and ensure that object orientation is followed.

Be careful that frameworks (like Hibernate) may differ from other implementations of the Specification (like JPA) and that additional effort is required if you switch to another implementation.

Alternative Approach

How would an alternative approach without getters and setters look like? As stated above, domain objects should fulfill business needs with domain-specific methods and apply real object orientation. Thus, the internal data is protected from direct access, but instead the object offers business related methods which operate on the data. Here is a simple example:

1public class Employee {
2    enum EmploymentState{
3      CURRENT_EMPLOYEE, FORMER_EMPLOYEE;
4    }
5 
6    private boolean accessToOfficeBerlin;
7    private boolean accessToWebapp;
8    private double salary;
9    private EmploymentState employmentState;
10    private Date dismissalDate;
11 
12    public void dismiss(){
13      if(employmentState == EmploymentState.FORMER_EMPLOYEE){
14        throw new IllegalStateException("employee already dismissed");
15      }
16 
17      employmentState = EmploymentState.FORMER_EMPLOYEE;
18      dismissalDate = new Date();
19 
20      accessToOfficeBerlin = false;
21      accessToWebapp = false;
22 
23      salary = 0.0;
24    }
25  }

If the dismissal would be done via setter-methods, the developer could forget to set a property. This could break invariants, like having a dismissal date for former employees.

A special type of data objects are value objects. These represent a value without identity, rather than an entity. A value object is characterized only by its attributes. Examples are colors, dates or amounts of money. They are often used  as parameters. Value objects should be immutable for several reasons. It simplifies development, since instances could be easily shared and passing them as parameters has no risk of unintentional manipulation. Being immutable also stresses the meaning of a value, represented by an instance. Manipulation of the value results in a new value and therefore in a new instance. An immutable object can not have setters. Instead it has methods with meaningful names to construct new objects.

The Java 8 Date/Time API is an example built around immutable instances:

1LocalTime now = LocalTime.now();
2  System.out.println(now);
3  LocalTime in15Minutes = now.plusMinutes(15);// creates a new object
4  System.out.println(now);// prints the same as the statement above

In addition to value objects there are also DTOs. Such objects are used on system boundaries to transfer pure data between systems. Examples might be a mapping to a database or transferring data as XML/JSON. As shown above, you should verify that the framework you use really needs getters and setters. If an object can be completely created by its constructor, then such an object doesn’t even have to be mutable. If you can not make the fields final it may still be possible to reduce the source code size and decrease the complexity by avoiding the accessor methods and use just public fields. Such mutable public fields should not be a problem for DTOs, as these objects have a very short lifetime and are not used in other parts of the application. Be aware that there is a risk that DTOs in general are used not only on the system boundaries but also within the application to a larger extent which may lead to bad design.

Conclusion

So does it mean you can avoid writing getters and setters at all? Not really. There are situations where they can be used safely. Especially for the display of data (the V in MVC) there is a need to access data and a simple getter does this job very well. Getters may be more relevant at this place than setters — you don’t need to generate both together!

To have these accessor methods is also fine if a framework really needs them. But in such cases  it is possible to decouple this code from the rest of the application to prevent setters from being used in other parts.

You should always be aware of the risks involved when accessor methods are added and ensure that you clearly understand the need, before you add and use such methods. Don’t accept the reasons “they are easy to generate” and “everybody does it this way”. Use domain-specific terminology and business logic and by doing so avoid anemic domain models.

share post

Likes

0

//

More articles in this subject area

Discover exciting further topics and let the codecentric world inspire you.

//

Gemeinsam bessere Projekte umsetzen.

Wir helfen deinem Unternehmen.

Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.

Hilf uns, noch besser zu werden.

Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.