Why does java.time use 'of' instead of 'new' for dates? [duplicate]
Asked Answered
T

3

5

In the new date package in Java 8, we changed from using "new Date()" to "LocalDate.of()".

Date d = new Date(year, month, dayOfMonth);            //Old way
LocalDate d2 = LocalDate.of(year, month, dayOfMonth);  //new way

When you want a new object you usually use the new keyword. This is an intuitive way to create a new object.

Sometimes, when you need a singleton with delayed initialization you can use a static method to get the instance. In this case you must call it getInstance() so developers know what to expect.

This new syntax makes the code less intuitive. It forces you to learn how to deal with specific objects instead of simply using them.

Are there any good reasons under the hood for this change?

Tantalite answered 24/1, 2020 at 8:51 Comment(7)
It's called the builder patternExerciser
The use of static methods like this is actually fairly common, especially with immutable classes. Also, the package documentation of java.time has a section named Design notes (non normative), which says the of prefix is used for "static factory methods" as part of the hope of making the API more manageable.Doggery
Builder pattern helps you to set multiple variables in an immutable object before instantiate it. In this case you set all the parameters in a single method, I don´t think it is the same case.Tantalite
In the documentation is fairly clear, the point is: the change seems less usable than the previous version, so there must be a good reason to do this change, I wonder whyTantalite
Keep in mind that not just of replaced new. There's also factory methods such as LocalDate#ofInstant(Instant,ZoneId), LocalDate#ofYearDay(int,int), LocalTime#ofNanoOfDay(long), LocalTime#ofSecondOfDay(long), LocalDateTime#ofEpochSecond(long,int,ZoneOffset) and so on. Reading those method names is much clearer than each class having 5+ constructors and having to determine which constructor is being used, and what it does, by tediously inspecting the arguments.Doggery
Factory methods are widespread in Java 8 and 9 and were also used in earlier Java versions. To mention a few: DatatypeFactory.newInstance() (since Java 1.5). Integer.valueOf(). IntStream.of().Fonda
Intuitive? I beg to differ. The old way isn’t more intuitive per se, but feels like it because your familiar with it. If there is a difference, new is intuitive for guaranteeing a new instance, while of is intuitive for not caring whether you get a new instance or an existing one. And since LocalDate and friends are immutable and value-based, you do not care.Fonda
F
5

Usually static factory methods are preferred to constructors for several reasons,

  • They have a name and that makes your code much more readable.
  • static factory methods can have covariant return types, but constructor can't. This allows your API users to work with the interface, not the implementation, allowing you more room to change the concrete types in future releases. Also it reduces the conceptual surface area of the API too.
  • Unlike constructors, static factory methods can do certain optimizations. On the contrary, every time you call a constructor, you expect it to return a new object. For an instance, check out the implementation of the static factory method Collections.emptyList();
public static final List EMPTY_LIST = new EmptyList<>();

public static final <T> List<T> emptyList() {
    return (List<T>) EMPTY_LIST;
}

It eagerly creates a singleton list and returns it on every invocation saving unnecessary overhead that would occur when constructors were used.

Due to the above reasons, static factories are preferred to constructors in today's programming. However, you can still use the new keyword with constructors as circumstances warrant.

Fijian answered 24/1, 2020 at 9:34 Comment(2)
And it allows to hide the actual implementation class, EmptyList in the example, from the public API. In case of java.time, these classes are further marked explicitly as value-based class.Pungent
Good catch, I have added it to the point 2. Appreciate it. Thanks !Fijian
S
2

There is a problem with new: your code depends on the concrete class. Thus changing/evolving the code is a more difficult task.

A factory is much more manageable because (1) the user code is not dependant on the concrete type (2) the code that choose the concrete type is in a single place. Then the final code is much more secure/evolvable, etc.

getInstance() is not a good name for a factory, it is more the name of a singleton method. "get" is not intended to create something, just to get something already existant. createInstance or create is much more explicit for factories (many of them, even in Java are called such - examine createImage and alike). But alas, for reasons explicited by @slaw in comments, you need to be more precise to help the user choose between argument semantic, thus things like createFromYears, createFromMinutes, etc. That may be slightly verbose, then of seems to be a good compromise. Finally it is more easy (IMHO) to use of*() than createImage() with a lot of parameters to explain what arguments are...

Steverson answered 24/1, 2020 at 10:16 Comment(1)
ZoneId.of() is a nice example of not depending on the concrete class. What it gives you, is not a ZoneId, but an instance of a subclass.Fonda
D
1

There a design pattern called Fluent interface, which allows "chaining" of methods, see Wikipedia. It's supposed to very readable, imitating natural speech ("get x of y with z").

It's not especially chaining in your example (given it consists of only one method call), but it makes sense to use the same paradigm everywhere.

Dilate answered 24/1, 2020 at 9:0 Comment(1)
I didn’t get it exactly. What would have hindered method chaining with new like new LocalDate(year, month, dayOfMonth).with(DayOfWeek.THURSDAY).atStartOfDay()? If they had preferred this designed.Fonda

© 2022 - 2024 — McMap. All rights reserved.