3

There's various ways to build an object in Java. For my use case let's just assume that I have an abstract Person class with private attribute fields and a subclass called Tom.

1. Getters/Setters

A no-arg constructor, setters, and getters in the Person class. Can be simplified by using Project Lombok's @Data annotation.

Tom tom = new Tom(); // Subclass of Person
tom.setName("Tom");
tom.setAge(42);

I've read that unless working with a framework, this method should be avoided.

2. Parameterized Constructor

Builds the object using constructor parameters. Excessive constructor overloading for big objects with various optional and required attributes. Can also be simplified using Project Lombok's various constructor annotations.

Tom tom = new Tom("Tom", 42);

Then, Tom's constructor would make a call to super("Tom", 42). In Person, you could make use of constructor overloading if there are many optional parameters.

3. Builder Pattern

Because constructor overloading can get real messy real fast, Josh Bloch presented the Builder Pattern. Again, this can be simplified with Project Lombok's @Builder annotation. This is how it would look with a non-abstract Person class:

Person person = new Person.Builder().name("Tom").age(42).build();

Trying to use the builder pattern together with inheritance is difficult (but not impossible) as others have said before me.


I'm sure there are many more ways to build an object, but I only listed the ones I am familiar with.

My question is: Using Java 8 or even Java 9 functionality, what is the absolutely best way to build a an object that is a subclass of an abstract superclass with required and optional attributes?

A lot of tutorials and questions on SO are outdated, and probably do not enforce what is considered as best practice nowadays.

Mikusch
  • 125
  • 1
  • 8

1 Answers1

2

What is best practice is unfortunately very subjective in Java and OO, but I try to list some mostly objective criteria:

  1. Whether the class is a subclass or not should not influence the API of that class at all, including how its constructor should look like.

  2. An object should be ready for use after construction. That means all of its public methods should be working after the constructor. This disqualifies your option #1. (Unless forced by unfriendly frameworks).

  3. Have one "main" constructor that does the work, and have all other constructors call this one. Unless you have a superclass that also has many constructors in which case things get more complicated, you most likely have to mirror what the superclass wants.

  4. It should be easy and straight forward to use. This means, it is preferable to use constructors vs. builders unless using them becomes difficult. If there are many parameters (for me more than 3-4), or there are just too many combinations of them possible (again, for me more than 3-4), or there is a depth of construction (has to construct other objects first), then I would use a builder.

  5. Don't under any circumstances use Optional as arguments.

  6. Don't use Lombok. It may make your (the writer's) life easier, but will mess with all those who have to read the code. Remember, we read code much more often than write! (This may be a debatable point)

  7. Inheritance is tricky and should be avoided if at all possible. Don't use it to share code, that is an anti-pattern. That is true for builders too. You can write a builder that can return different implementation classes, but it does not have to (should not) subclass other builders to do that.

I possibly forgot some rules, but other than these, there is no new groundbreaking syntax in Java 8 and 9 to simplify construction.

Robert Bräutigam
  • 7,514
  • 1
  • 20
  • 38