问题描述
我计划编写一个extends java.time.YearMonth
类,目的是通过一种使我能够流式传输YearMonth
的{{1}}的方法来扩展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 class
是不可扩展的时,该计划立即失败了。
我当然可以写一个像这样的课
final class
但这不是我想要的,因为它需要我实例化{{1}的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
){{特定年份中特定月份的1}} s。
JavaDocs of YearMonth
仅声明 是stream
,而不是为什么是filter
:
LocalDate
...
YearMonth是一个不可变的日期时间对象,表示年份和月份的组合。
...
为什么将final
设为final
?
或更准确地说:public final class YearMonth
比YearMonth
有什么好处?
我无法想象任何原因...
我知道,要回答这一问题,必须了解可能在www某个地方公开的设计决策,但是,不幸的是,我没有这种见识,并且到目前为止,我还没有找到任何资料。
在Kotlin中,这无关紧要,因为您可以编写扩展功能而不必从该final
继承。这是Kotlin的一个不错的功能,但是Java(暂时没有),我拒绝为此编写包装类。
我还可以问为什么final class YearMonth
中没有这样的方法,或者当class YearMonth
在Java 9中获得class
时却没有添加这种方法,但这是第二个问题。单个帖子,通常会皱着眉头(和否决或不赞成投票),因此我稍后可能会在其他帖子中提出。
我当前的解决方案是执行上述代码所执行的YearMonth
,但是我必须将实例传递给LocalDate
方法,而不是直接使用该方法。这可以满足我的要求,但仍然不是我认为完美的解决方案。
解决方法
The documentation of YearMonth
确实是这样说的,但间接是这样的:
这是value-based类;在
==
的实例上使用身份敏感的操作(包括引用相等性(YearMonth
,身份哈希码或同步)可能会产生不可预知的结果,应避免使用。
基于值的类
某些类(例如
java.util.Optional
和java.time.LocalDateTime
)是基于值的。基于值的类的实例:
- 是最终的且不可变的(尽管可能包含对可变对象的引用);
- 具有
equals
,hashCode
和toString
的实现,它们仅根据实例的状态而不是根据其标识或任何其他对象或变量的状态来计算;- 不使用身份敏感的操作,例如实例之间的引用相等(
==
),实例的身份哈希码或实例的固有锁上的同步;- 仅基于
equals()
被认为是相等的,而不是基于引用相等(==
)被认为是相等的;- 没有可访问的构造函数,而是通过工厂方法实例化的,该方法不承诺返回实例的身份;
当相等时,- 是可自由替换的,这意味着在任何计算或方法调用中,根据
x
互换相等的两个实例y
和equals()
不会产生明显的行为变化。如果程序试图将两个引用区分为基于值的类的相等值,则可能会产生不可预测的结果,无论是直接通过引用相等还是间接通过调用同步,身份哈希,序列化或任何其他身份敏感的方法机制。在基于值的类的实例上使用这种对身份敏感的操作可能会产生不可预知的影响,应避免使用。
这里没有明确说明,但是子类化会与这些要点相抵触,因为这会导致实例表示相同值的可能性(就基类的状态而言),但是当实例不表示时,它们可以自由替换具有相同的类型。同样,即使当类不是final
时,仅提供返回未指定身份实例的工厂方法的概念也将不允许子类,因为子类需要可访问的构造函数。
您可能会看到基于值的类等同于原始值;您不能将int
子类化,因此也不能将YearMonth
子类化,因为它仅表示特定值(只是强类型化),并且所有表示相同值的YearMonth
实例无论是由不同的对象实例还是由单个实例表示,都应该是相同的。这样,使用Java的实际值类型就可以为将来开辟道路。
因为该类的实例保证是不可变的。
如果允许子类,则无法保证。
来自YearMonth class documentation:
* {@code YearMonth} is an immutable date-time object that represents the combination
* of a year and month.