I planned to write a class that extends java.time.YearMonth
with the purpose to extend the YearMonth
with a method that enables me to stream the LocalDate
s of that YearMonth
:
public class ExtendedYearMonth extends YearMonth {
public Stream<LocalDate> days() {
LocalDate firstOfMonth = this.atDay(1);
LocalDate lastOfMonth = firstOfMonth.with(TemporalAdjusters.lastDayOfMonth());
return firstOfMonth.datesUntil(lastOfMonth);
}
}
Well, that plan instantly failed when I found YearMonth
to be a final class
⇒ final class
es are not extensible.
I could of course write a class like this one
public class ExtendedYearMonth {
private YearMonth yearMonth;
// parameterized constructor, getters and setters omitted for brevity
public Stream<LocalDate> days() {
LocalDate firstOfMonth = this.yearMonth.atDay(1);
LocalDate lastOfMonth = firstOfMonth.with(TemporalAdjusters.lastDayOfMonth());
return firstOfMonth.datesUntil(lastOfMonth);
}
}
but that's not what I wanted because it requires me to instantiate a YearMonth
and an ExtendedYearMonth
just to stream
(and filter
, the main purpose) the LocalDate
s of a specific month in a specific year.
The JavaDocs of YearMonth
just state that it's final
, but not why it is final
:
public final class YearMonth
...
YearMonth is an immutable date-time object that represents the combination of a year and month.
...
YearMonth
made final
?Or more accurate : What's the benefit of final class YearMonth
over a class YearMonth
?
I cannot imagine any reason...
I know, to answer this one is required to have insight to the design decisions that may be public somewhere in the www, but, unfortunatley, I don't have this insight and I haven't found a source so far.
In Kotlin, this doesn't matter because one can write an extension function without having to inherit from that class
. That's a nice feature of Kotlin, but Java doesn't have this (for now) and I refuse to write a wrapper class for this.
I could also ask why such a method isn't available in YearMonth
or wasn't added when LocalDate
got datesUntil
in Java 9, but that would be a second question in a single post, which is generally frowned (and down- or close-voted) upon, so I may ask that later in a different post.
My current solution is a public static Stream<LocalDate> daysOf(YearMonth yearMonth)
which does what the above code does, but I have to pass an instance to a static
method instead of directly using a method of it. That's working for my requirement, but it's still not what I consider a near-to-perfection solution.
The documentation of YearMonth
does say it, but indirectly:
This is a value-based class; use of identity-sensitive operations (including reference equality (
==
), identity hash code, or synchronization) on instances ofYearMonth
may have unpredictable results and should be avoided.
Whereas value-based says:
Value-based Classes
Some classes, such as
java.util.Optional
andjava.time.LocalDateTime
, are value-based. Instances of a value-based class:
- are final and immutable (though may contain references to mutable objects);
- have implementations of
equals
,hashCode
, andtoString
which are computed solely from the instance's state and not from its identity or the state of any other object or variable;- make no use of identity-sensitive operations such as reference equality (
==
) between instances, identity hash code of instances, or synchronization on an instances's intrinsic lock;- are considered equal solely based on
equals()
, not based on reference equality (==
);- do not have accessible constructors, but are instead instantiated through factory methods which make no committment as to the identity of returned instances;
- are freely substitutable when equal, meaning that interchanging any two instances
x
andy
that are equal according toequals()
in any computation or method invocation should produce no visible change in behavior.A program may produce unpredictable results if it attempts to distinguish two references to equal values of a value-based class, whether directly via reference equality or indirectly via an appeal to synchronization, identity hashing, serialization, or any other identity-sensitive mechanism. Use of such identity-sensitive operations on instances of value-based classes may have unpredictable effects and should be avoided.
It’s not stated explicitly here, but subclassing would contradict these points, as it would lead to the possibility of instances representing the same value (in terms of the baseclass’ state), but not be freely substitutable, when they don’t have the same type. Also, even when the class was not final
, the concept of only providing factory methods returning instances of unspecified identity would not allow subclasses, as subclasses require an accessible constructor.
You may see value-based classes as an equivalent to primitive values; you can’t subclass an int
, so you can’t subclass a YearMonth
, as it only represents a specific value (just strongly typed) and all YearMonth
instances representing the same value are supposed to be the same, whether represented by different object instances or a single instance. This keeps the path open to a future with real value types in Java.