This is a Pattern design known as Static Factory Method.
The idea is not to "swindle" anything, but to provide some features that would not be possible only with the constructor.
One of the possibilities was cited in another answer: control the number of instances created. An example is in the JVM itself, in the class java.lang.Integer
, that internally has a cache which holds values between -128 and 127. You can refer to the source code and see for yourself, but finally, the current implementation (on the date I write this reply) is:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
That is, the method valueOf
is serving as a Static Factory method. I might even use new Integer(1)
, only then I would always be creating a new instance, while using Integer.valueOf(1)
, i get the instance that is in the cache (ie multiple calls to valueOf
would use the same instance, while multiple calls to the constructor would create multiple instances).
In some cases one can leave the builder private
, so the only way to create instances is by using the Static Factory method. This is a decision of design API: you want to control at all costs the creation of new instances, or want to let the programmer decide?
Another advantage is to give more semantics to the code. For example (using one of the answers in the question already cited at the beginning), if I had a class representing the coordinates on a Cartesian plane, I could create an instance using Cartesian or polar coordinates:
public static Coordinate createFromCartesian(double x, double y) {
...
}
public static Coordinate createFromPolar(double distance, double angle) {
...
}
Internally, the class can store the coordinates in the way that suits it best (and the above methods make the necessary conversions in each case).
Already using only one constructor would not be possible, because if I declare it as public Coordinate(double x, double y)
, could not know which of the two versions I am calling (maybe would have to have a third parameter indicating whether are Cartesian or polar coordinates).
Using another example, you could have a class Temperatura
, can build it from a value in Celsius, Kelvin or Fahrenheit:
public class Temperatura {
private double kelvin; // valor é internamente gravado em Kelvin
private Temperatura(double kelvin) {
this.kelvin = kelvin;
}
public static Temperatura fromCelsius(double graus) {
// converte para Kelvin
return new Temperatura(celsiusToKelvin(graus));
}
public static Temperatura fromKelvin(double graus) {
return new Temperatura(graus);
}
public static Temperatura fromFahrenheit(double graus) {
// converte para kelvin
return new Temperatura(fahrenheitToKelvin(graus));
}
}
Internally the value is stored in Kelvin, and each method receives its respective value and makes the necessary conversions. Validations could also be included (for example, if the value in Kelvin is negative, gives error, etc).
Another detail is that so you can change internal implementation details without affecting the use of Static Factory methods. For example, instead of converting everything to Kelvin, I could store the value of the shape it is, and create another field indicating which scale is used (Celsius, Fahrenheit, etc.). The constructor could change without problem, but for those who use static methods, nothing would change.
A similar example is in the JVM itself: na class java.time.LocalDate
, the builder is just that:
private LocalDate(int year, int month, int dayOfMonth) {
this.year = year;
this.month = (short) month;
this.day = (short) dayOfMonth;
}
That is, it only receives the values of the day, month and year. But if we look at the various Static Factory methods, let’s find something like this:
public static LocalDate of(int year, Month month, int dayOfMonth) {
YEAR.checkValidValue(year);
Objects.requireNonNull(month, "month");
DAY_OF_MONTH.checkValidValue(dayOfMonth);
return create(year, month.getValue(), dayOfMonth);
}
public static LocalDate ofInstant(Instant instant, ZoneId zone) {
Objects.requireNonNull(instant, "instant");
Objects.requireNonNull(zone, "zone");
ZoneRules rules = zone.getRules();
ZoneOffset offset = rules.getOffset(instant);
long localSecond = instant.getEpochSecond() + offset.getTotalSeconds();
long localEpochDay = Math.floorDiv(localSecond, SECONDS_PER_DAY);
return ofEpochDay(localEpochDay);
}
That is, each specific method validates the different data it receives, and in the end all end up calling the constructor: in this case, the method create
called for of
makes additional validations to verify if the date is valid (if it is leap year and February has 29 days, for example, in addition to other cases), the method ofEpochDay
makes a series of calculations to get the correct values of day, month and year, etc (and in the end, always end up calling the builder).
With this you gain more flexibility, as it gives you several different options to create the instances (besides giving more semantics, with more meaningful names that would not be possible only with a constructor).
Going further, using a Static Factory method you can return sub-classes of the class in question, which is not possible with a constructor, since it always returns an instance of the class you are building.
An example is the interface java.util.List
, which from Java 9 has the method of
:
List<String> list = List.of("a", "b");
System.out.println(list.getClass()); // class java.util.ImmutableCollections$List12
The created list is an instance of java.util.ImmutableCollections$List12
(this is not a public class and I can not use it directly, but nothing prevents the Static Factory method return an instance of it). Finally, this class (whose source can be consulted here) was used internally by Static Factory method (perhaps because it is an "optimized" implementation for this specific case, or any other reason at all), which would not be possible with a constructor.
The same happens with methods such as Collections.unmodifiableList
(that returns a list that cannot be modified, that may or may not be instance of a specialized class, and not necessarily public), and being internal implementation details, can change without affecting who uses such methods. For example, the above code has been tested in Java 12, but it may be that in other versions this changes (they can switch to another class, etc).
Anyway, the general idea is this. You can consult more examples here and here (some I adapted in this answer, but the links have more cases).
See also:
The answer itself is good, but where are the examples? the references? see how to write a good answer?
– gleisin-dev