deH*_*aar 0 java final class java-stream java-time
我计划编写一个类,extends java.time.YearMonth目的是YearMonth使用一种方法来扩展LocalDates ,该方法使我能够流式传输其中的s 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);
}
}
Run Code Online (Sandbox Code Playgroud)
好吧,当我发现YearMonth是一个final class? final classes 不可扩展。
我当然可以写一个这样的类
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);
}
}
Run Code Online (Sandbox Code Playgroud)
但这不是我想要的,因为它需要我将 aYearMonth和 an实例化ExtendedYearMonth到stream(以及filter主要目的)LocalDate特定年份中特定月份的s 。
在的JavaDoc中YearMonth只是状态是它final,而不是为什么它是final:
public final class YearMonth
...
YearMonth 是一个不可变的日期时间对象,表示年和月的组合。
...
YearMonth制造final?或更准确地说:final class YearMonth超过 a 有class YearMonth什么好处?
我想不出有什么理由...
我知道,要回答这个问题,需要深入了解可能在 www 某处公开的设计决策,但不幸的是,我没有这种洞察力,到目前为止我还没有找到来源。
在 Kotlin 中,这无关紧要,因为您可以编写扩展函数而无需从其继承class。这是 Kotlin 的一个很好的特性,但是 Java 没有这个(目前),我拒绝为此编写包装类。
我还可以问,为什么这种方法不使用YearMonth或者不加入LocalDate了datesUntil在Java中9,但是这将是在一个单一的岗位,这是一般不赞成第二个问题(和下或近投票)后,所以我可能会在稍后的另一篇文章中问这个问题。
我当前的解决方案是public static Stream<LocalDate> daysOf(YearMonth yearMonth)执行上述代码的操作,但我必须将实例传递给static方法,而不是直接使用它的方法。这符合我的要求,但我认为这仍然不是接近完美的解决方案。
的文档YearMonth确实说了,但间接地:
这是一个基于值的类;
==对 的实例使用身份敏感操作(包括引用相等 ( )、身份哈希码或同步)YearMonth可能会产生不可预测的结果,应避免。
而基于价值的说:
基于值的类
一些类,例如
java.util.Optionalandjava.time.LocalDateTime,是基于值的。基于值的类的实例:
- 是最终的和不可变的(尽管可能包含对可变对象的引用);
- 具有、 和 的实现
equals,它们仅根据实例的状态计算,而不是根据其身份或任何其他对象或变量的状态计算;hashCodetoString- 不使用对身份敏感的操作,例如
==实例之间的引用相等 ( )、实例的身份哈希码或实例内在锁上的同步;- 被认为相等,仅基于
equals(),而不是基于引用相等 (==);- 没有可访问的构造函数,而是通过工厂方法实例化,不承诺返回实例的身份;
- 在相等时可以自由替换,这意味着交换任何两个实例
x并且在任何计算或方法调用y中都相等的实例equals()不应产生明显的行为变化。如果程序试图区分对基于值的类的相等值的两个引用,无论是直接通过引用相等还是间接通过同步、身份散列、序列化或任何其他身份敏感机制,程序可能会产生不可预测的结果。在基于值的类的实例上使用这种对身份敏感的操作可能会产生不可预测的影响,应该避免。
这里没有明确说明,但是子类化会与这些点相矛盾,因为它会导致实例表示相同值的可能性(就基类的状态而言),但不能自由替换,当它们不具有相同的值时类型。此外,即使类不是final,仅提供返回未指定身份实例的工厂方法的概念也不允许子类,因为子类需要可访问的构造函数。
您可能将基于值的类视为与原始值等价的类;你不能子类化 an int,所以你不能子类化 a YearMonth,因为它只代表一个特定的值(只是强类型),并且YearMonth代表相同值的所有实例都应该是相同的,无论是由不同的对象实例表示还是由单个实例。这为 Java 中的真实值类型的未来开辟了道路。