为什么 java.time.YearMonth 进入决赛 class?

Why was java.time.YearMonth made a final class?

我计划写一个 class extends java.time.YearMonth 的目的是使用一种方法扩展 YearMonth 使我能够流式传输 LocalDate 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);
    }
}

好吧,当我发现 YearMonth 是一个 final classfinal classes 是不可扩展的 .

我当然可以写一个这样的 class

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);
    }
}

但这不是我想要的,因为它需要我实例化一个 YearMonth 和一个 ExtendedYearMonth 只是为了 stream(和 filter,主要目的) LocalDate 特定年份的特定月份。

JavaDocs of YearMonth 只是说明 它是 final,而不是 为什么 它是 final:

public final class YearMonth
...
YearMonth is an immutable date-time object that represents the combination of a year and month.
...

为什么 YearMonth 变成了 final

或更准确地说:final class YearMonthclass YearMonth 有什么好处?
我想不出任何理由...

我知道,回答这个问题需要深入了解可能在 www 某处 public 的设计决策,但不幸的是,我没有这种洞察力,我也没有目前已找到来源。

在 Kotlin 中,这无关紧要,因为可以编写扩展函数而不必继承 class。这是 Kotlin 的一个很好的特性,但是 Java 没有这个(目前),我拒绝为此写一个包装器 class。

我还可以问为什么这样的方法在 YearMonth 中不可用,或者当 LocalDate 在 Java 9 中得到 datesUntil 时没有添加,但是将是单个 post 中的第二个问题,通常不赞成(并反对或反对),因此我稍后可能会在另一个 post.

中提出这个问题

我当前的解决方案是 public static Stream<LocalDate> daysOf(YearMonth yearMonth),它执行上述代码的操作,但我必须将实例传递给 static 方法,而不是直接使用它的方法。这符合我的要求,但它仍然不是我认为接近完美的解决方案。

因为 class 的实例保证 不可变

如果允许 subclasses 则无法保证。

来自YearMonth class documentation

* {@code YearMonth} is an immutable date-time object that represents the combination
* of a year and month. 

The documentation of YearMonth 确实这么说了,但是是间接的:

This is a value-based class; use of identity-sensitive operations (including reference equality (==), identity hash code, or synchronization) on instances of YearMonth may have unpredictable results and should be avoided.

value-based 说:

Value-based Classes

Some classes, such as java.util.Optional and java.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, and toString 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 and y that are equal according to equals() 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.

这里没有明确说明,但是 subclassing 会与这些点相矛盾,因为它会导致实例代表相同值的可能性(根据 baseclass' 状态),但不能自由替换,当它们不具有相同的类型时。此外,即使 class 不是 final,仅提供返回未指定身份实例的工厂方法的概念也不允许 subclasses,因为 subclasses 需要一个可访问的构造函数。

您可能会看到 value-based classes 等同于原始值;你不能子class一个int,所以你不能子class一个YearMonth,因为它只代表一个特定的值(只是强类型)和所有YearMonth 表示相同值的实例应该是相同的,无论是由不同的对象实例表示还是由单个实例表示。这使通往未来的道路在 Java.

中具有真实值类型