java - 为什么 java.time.YearMonth 做了最后一堂课?
问题描述
我计划编写一个类,extends java.time.YearMonth
目的是YearMonth
使用一种方法来扩展 s ,该方法使我能够流式传输LocalDate
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);
}
}
好吧,当我发现⇒ es is not extensibleYearMonth
时,该计划立即失败。final class
final 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);
}
}
但这不是我想要的,因为它需要我实例化 aYearMonth
和 anExtendedYearMonth
以stream
(和filter
主要目的)LocalDate
特定年份中特定月份的 s 。
JavaDocsYearMonth
只是声明它是,final
但不是为什么它是final
:
public final class YearMonth
...
YearMonth 是一个不可变的日期时间对象,表示年和月的组合。
...
为什么被YearMonth
制作final
?
final class YearMonth
或更准确地说: over a 有什么好处class YearMonth
?
我想不出任何理由...
我知道,要回答这个问题,需要对可能在 www 某处公开的设计决策有洞察力,但是,不幸的是,我没有这种洞察力,而且到目前为止我还没有找到来源。
在 Kotlin 中,这无关紧要,因为可以编写扩展函数而无需从该函数继承class
。这是 Kotlin 的一个很好的特性,但是 Java 没有这个(现在),我拒绝为此编写一个包装类。
我还可以问为什么这样的方法在 Java 9 中不可用YearMonth
或没有添加,但这将是单个帖子中的第二个问题,通常不赞成(并且反对或接近投票) ,所以我稍后可能会在不同的帖子中问这个问题。LocalDate
datesUntil
我当前的解决方案是public static Stream<LocalDate> daysOf(YearMonth yearMonth)
执行上述代码的操作,但我必须将实例传递给static
方法,而不是直接使用它的方法。这符合我的要求,但它仍然不是我认为接近完美的解决方案。
解决方案
的文档YearMonth
确实这么说,但间接地:
这是一个基于值的类;对 的实例使用身份敏感操作(包括引用相等 (
==
)、身份哈希码或同步)YearMonth
可能会产生不可预知的结果,应该避免。
而基于价值的说:
基于值的类
一些类,例如
java.util.Optional
和java.time.LocalDateTime
,是基于值的。基于值的类的实例:
- 是最终的和不可变的(尽管可能包含对可变对象的引用);
- 具有、 和的实现
equals
,这些实现仅根据实例的状态计算,而不是从其标识或任何其他对象或变量的状态计算;hashCode
toString
- 不使用身份敏感操作,例如
==
实例之间的引用相等 ()、实例的身份哈希码或实例的内在锁上的同步;- 仅基于 被认为相等
equals()
,而不是基于引用相等 (==
);- 没有可访问的构造函数,而是通过工厂方法实例化,这些方法不承诺返回实例的身份;
- 在相等时可以自由替换,这意味着在任何计算或方法调用中交换任何两个实例
x
并且y
相等的实例equals()
不应该产生可见的行为变化。如果程序试图区分对基于值的类的相等值的两个引用,无论是直接通过引用相等还是间接通过诉诸同步、身份散列、序列化或任何其他身份敏感机制,它都可能产生不可预知的结果。在基于值的类的实例上使用这种对身份敏感的操作可能会产生不可预知的影响,应该避免。
这里没有明确说明,但是子类化会与这些点相矛盾,因为它会导致实例表示相同的值(就基类的状态而言),但当它们不具有相同的值时不能自由替换类型。此外,即使类 not final
,仅提供返回未指定标识实例的工厂方法的概念也不允许子类,因为子类需要可访问的构造函数。
您可能会将基于值的类视为原始值的等价物;你不能继承 an int
,所以你不能继承 a YearMonth
,因为它只代表一个特定的值(只是强类型),并且YearMonth
代表相同值的所有实例都应该是相同的,无论是由不同的对象实例表示还是单个实例。这为 Java 中具有真实值类型的未来开辟了道路。
推荐阅读
- javascript - 用占位符定义一个常量
- ios - 如何防止在 iOS 应用程序中备份用户数据?
- android - 重新启动时自动重新启动应用程序无法在 Rooted Android 6 设备上运行
- javascript - 为什么 isNaN('') 或 isNaN("") 为假(单引号或双引号被视为有效数字)?
- lua - 在 Lua 表中查找重复值
- html - Woocommerce Floating Bar 有额外的 Margin-Top
- c# - 在asp.net核心中发送带有防伪令牌的post ajax请求不要访问控制器方法
- fortran - 通过 type(c_ptr) 虚拟变量传递外部函数和自定义数据类型
- command - Spyder 新手,不确定为什么 < 在我的脚本中无法触发“小于”
- amazon-cloudformation - 在 2 个区域部署 2 个依赖的 cloudformation 堆栈部分