I have 4 instance variables that can be initialized by default, is the code written below appropriate? The number of constructors is growing a terrible pace, will I use the Builder object that have getters and setters?
class Example {
private int a;
private String s;
private Object object;
private boolean aBoolean;
public Example(int a1, String s1, Object object1, boolean aBoolean1) {
a = a1;
s = s1;
object = object1;
aBoolean = aBoolean1;
}
public Example(int a1, String s1, Object object1) {
this(a1, s1, object1, true);
}
public Example(int a1, String s1) {
this(a1, s1, new Object(), true);
}
public Example(int a1,boolean aBoolean1) {
this(a1, "a String", new Object(), aBoolean1);
}
// ...(for all other combinations)
}
Essentially, no. As a concept this doesn't scale. In two ways.
Why your style doesn't work
Combinatory explosion
Imagine you have 6 fields, and they all have default values. That means you're creating 2-to-the-6 different constructors (64 constructors). Aside from the fact that this is nuts on its face and certainly unmaintainable, even if you decide to use some automated tooling or annotation processors to generate them, there's a 65536 limit to the number you can have due to class file limits.
Type conflicts
Imagine you have 2 fields (
aandb), and they all have default values. Both fields are of typeint. You can't do it - the constructor that takes onlyaand leavesbat default value conflicts with the constructor that takes onlyband leavesaat default value - both have the signatureYourType(int).The common solutions
Left-to-right only
Instead of creating the combinatory explosion, only pad on. So, given a hypothetical class with 3 fields (
int a; int b; String c;), you have 4 constructors:()(int a)(int a, int b)(int a, int b, String c)In other words, a given 'field' is required if any field 'below it' is also provided. Then order your fields in a sensible order. This keeps the number of constructors you have managable (if they're all defaulted, 1+n, which is linear to the number of fields you have, instead of a fac(n) explosion). It's also more or less what java programmers expect, and it matches various other languages that work similarly.
Only full-and-none
The only constructors that exist is the no-args one because various tools need it, and the all-args one.
Sensible
Think about the API, about how code that uses your class would operate. Is there a limited set of common and obvious ways to construct it? If yes, write those constructors. By definition there is no way to tell which ones you should write given a list of field types - you need to explain exactly what your type represents and what it does, it's "in the eye of the beholder". API design has a whiff of artistry to it. You decide how you think it should be used.
Builders
This is the true fire-and-forget works-for-every-situation solution. You write a builder. The nature of builders means that your API users (the code that invokes stuff on this class / on instances of this class) end up writing something like this.
The reason you don't build like that is not just because it makes it very tricky to make every argument optional. It's also that the call itself becomes unreadable. Is that a bridge built in 1937 with a max span of 1280 meters, or is that a bridge built in 1280 with a max span of 1937 meters? Can't tell from this call without having the javadoc or getting my IDE to highlight the names of those params.
instead, you make your library users write this:
This has all sorts of advantages:
fac(n)explosion - it's linear to the number of fields..buildDate(LocalDate.of(1937, 4, 19))and also.buildDate(1937, Month.APRIL, 19)- if you really want that, at least it doesn't make the combinatory explosion effect even worse.The downside is that it's a ton of boilerplate to write a builder and all the boilerplatey methods that are needed to make it work (not just a 'setter' in the build class for every field, but also
builder(),build(), a private constructor, and so on).Project Lombok generates all that for you though:
@Builderdocumentation. Even if you can't use lombok / do not like it, the docs contain an example of the 'vanilla' version of what@Buildergenerates, which serves as example of how to do it on your own.