Domanda

In the new java.time package the core classes are using the factory method of instead of a public constructor. Even though I like the cosmetics of the of method I can't see a good reason to not use a constructor. The documentation I've found just explains that this is the case and doesn't really go in to why it's the case.

Quote from tutorial:

Most of the classes in the Date-Time API create objects that are immutable, meaning that, after the object is created, it cannot be modified. To alter the value of an immutable object, a new object must be constructed as a modified copy of the original. This also means that the Date-Time API is, by definition, thread-safe. This affects the API in that most of the methods used to create date or time objects are prefixed with of, from, or with, rather than constructors, and there are no set methods.

È stato utile?

Soluzione

Factory methods allow simplifying the constructors - the difference which instance in time is represented by the Object can be separated from the calculation of the instant.

There are lots of factory methods, some doing String parsing, some converting from some other date or time representation, but all that can happen before the Object has to be created (if creating a new Object is even necessary at all - could also be a cached one).

Another big advantage is that factory methods can have descriptive names: the methods for parsing String representations are named e.g. LocalTime.parse - that is not possible with constructors.

Altri suggerimenti

It's not always necessary to create a new object when obtaining a value of an immutable type. A factory method may return an object that has already been created while a constructor call always creates a new object.

There are some other advantages to factory methods, but they're not as closely related to immutability and the Date-Time API. For instance, factory methods have names and they may return objects of some subtype.

I didn't design or write Java's Date-Time API, so I can't definitively say "why did they do it this way." I can however suggest two key reasons they should do it this way:

  1. Expressive power
  2. Parallel form

First, consider the context: Java's dates and time code is a large, complex package dealing with a very tricky subject matter. As common as "time" is, implementing the concept involves dozens of interpretations and formats, with variations across geographic locations (timezones), languages, calendar systems, clock resolutions, time scales, numerical representations, data sources, and intervals. Corrective deltas (e.g. leap years/days) are common, and can be irregularly inserted at any time (leap seconds). Yet the package is a core feature, likely to be widely used, and it's expected to deal with all those variations both correctly and precisely. It's a tall order. The result, not surprisingly, is a complex API including many classes, each with many methods.

Now, why use of methods rather than a "regular" class constructor? For example, why LocalDate.of(...), LocalDate.ofEpochDay(...), and LocalDate.ofYearDay(...) rather than just a handful of new LocalDate(...) calls?

First, expressive power: Individual named methods express the intent of the construction and the form of the data being consumed.

Assume for a moment that Java's method overloading is sufficient to allow the variety of constructors you want. (Without getting into a language-feature discussion around duck typing and single vs. dynamic vs. multiple dispatch I'll just say: sometimes this is true, sometimes not. For now, just assume there is no technical limitation.)

It might be possible for constructor documentation to state "when passed only a single long value, that value is interpreted as an epoch day count, not a year." If the low-level data types passed to the other constructors is different (e.g. only the epoch case uses long, and all the other constructors use int), you might get away with this.

If you looked at code calling a LocalDate constructor with a single long, it'd look very similar to code in which a year is passed in, especially with passing a year arguably the most common case. Having that common constructor might be possible, but it wouldn't necessarily lead to great clarity.

The API explicitly calling out ofEpochDay however, signals a clear difference in units and intent. Similarly LocalDate.ofYearDay signals that the common month parameter is being skipped, and that the day parameter, while still an int, is a dayOfYear not a dayOfMonth. Explicit constructor methods are intended to express what's going on with greater clarity.

Dynamically- and duck-typed languages such as Python and JavaScript have other means to signal alternate interpretations (e.g. optional parameters and out-of-band sentinel values), but even Python and JS developers often use distinguished method names to signal different interpretations. It's even more common in Java, given both its technical constraints (it's a statically typed, single-dispatch language) and its cultural conventions/idioms.

Second, parallel form. LocalDate could provide a standard Java constructor in addition to, or in place of, its two of methods. But to what end? It still needs ofEpochDay and ofDayYear for those use cases. Some classes do provide both constructors and the equivalent of ofXYZ methods--for example, both Float('3.14') and Float.parseFloat('3.14') exist. But if you need ofXYZ constructors for some things, why not use them all the time? That is simpler, more regular, and more parallel. As an immutable type, the majority of LocalDate methods are constructors. There are seven major groups of methods that construct new instances (respectively, the at, from, minus, of, parse, plus, and with prefixes). Of LocalDate's 60+ methods, 35+ are de facto constructors. Only 2 of those could be easily converted into standard class-based constructors. Does it really make sense to special-case those 2 in a way not parallel to all the others? Would client code using those two special case constructors be notably clearer or simpler? Probably not. It might even make client code more varied and less straightforward.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
scroll top