【问题标题】:Could Primitive Wrapper Classes (Integer, Double etc) and String be approximated as value based classes?原始包装类(整数、双精度等)和字符串可以近似为基于值的类吗?
【发布时间】:2021-01-26 22:26:13
【问题描述】:

Java 在 Java 8 中引入了 value based 类,并且只用它标记了少数 Java.Util(可选)和 Java.time 类。

基于值的类的标准定义为here

这些标准中的大多数(除了在身份敏感操作中使用它们的严格指导以及它们不应该具有可访问的构造函数这一事实)似乎适合原始包装类,如 Integer、Double(以及类似的不可变类,如 String) .我知道从技术上讲,开发人员可能已经将这些类与基于身份的操作一起使用(尽管他们不应该使用),因此现在将它们标记为 value based 可能会导致向后兼容性问题。但除此之外,还有什么其他原因导致在概念上,这些包装类不是基于值的吗?

我倾向于将它们视为基于价值的原因

当您定义Integer myInt = 5 时,您实际上只对值 5 感兴趣,而不是对持有该值的引用感兴趣(至少对于我能想到的大多数用例)。同样,当您说String myStr = "hello world" 时,您真正感兴趣的是值文字“hello world”,而不是它的引用。

【问题讨论】:

  • 在列出的条件中:没有可访问的构造函数。
  • 是的,那也是……编辑了问题。我很想知道是否还有其他根本原因?
  • 读到这里,我很好奇……什么可以调用基于 Integer 或 String 值的购买?
  • 仅供参考: String 不是“包装类”。
  • @Andreas 好吧,从技术上讲,它是一个数组的包装器……

标签: java java-8


【解决方案1】:

基本上,您指出了最重要的原因,为什么将这些类改造成 基于值的 是不可行的:向后兼容性

您可能已经注意到,原始包装类型的构造函数在 Java 9 中已被弃用,这将是朝着这个方向迈出的一步。尽管如此,仅不鼓励而不是禁止使用身份敏感操作,因此不能在此基础上进行破坏兼容性的更改。但是,可能会破坏身份敏感操作,这将是唯一可以从基于值的类中获得后续实际优势的事情。

对于StringBigIntegerBigDecimal 之类的类,JDK 开发人员甚至不敢采取弃用构造函数的步骤,很可能是因为那样会造成太大的干扰。对于某些构造函数,甚至没有等效的工厂方法。

但不仅仅是公共构造函数。

请参阅valueOf 方法的文档,the one of Integer 示例:

此方法将始终缓存 -128 到 127 范围内的值,包括在内,...

所以当使用工厂方法时,在某些情况下你仍然会得到一个指定的身份行为。

这将我们带到JLS §5.1.7

如果被装箱的值 p 是对 booleanbytecharshortintlong 类型的常量表达式 (§15.29) 求值的结果,并且结果是truefalse'\u0000''\u007f' 范围内的字符,或 -128127 范围内的整数,然后让 abp 的任意两次拳击转换的结果。 a == b 总是如此。

因此,即使是语言也指定了某些身份敏感操作的行为。

请注意,规范尽量不命名编译后代码将在实践中使用的valueOf 方法,而是制定自己的规则(正式仅适用于编译时常量),这并没有真正得到回报.正如this answer 文件,规范的那部分经历了多次重写,所以当任何人从字面上理解措辞时,保证会随着时间的推移而改变......

其他保证已经深入开发者的脑海:

JLS §15.29, Constant Expressions:

String 类型的常量表达式始终是“内部”的,以便使用 String.intern 方法共享唯一实例。

这是对无法拒绝的对象身份的保证。

有趣的是,JLS §15.18.1 表示:

String 对象是新创建的 (§12.5),除非表达式是常量表达式 (§15.29)。

不清楚这种严格的措辞是否是故意的,但正如所写,它指出对于非常量字符串连接,它必须产生一个具有不同标识的新对象。开发人员不应依赖的另一种指定行为。

因此,如果有人要设计一种没有遗留的新语言,那么首先将这些类型设计为值类型并没有错。设计者只需避免将所有这些保证放入过去被认为是个好主意的规范中。

【讨论】:

    【解决方案2】:

    基于值的类的目标是为真正的值类型奠定基础,即类似于C的struct的类型。

    值类型不是堆上的对象,所以它们不是使用new创建的,它们不是从Object派生的,所以它们没有对象监视器(锁) .

    这就是为什么以下 2 条规则是 基于值的定义的一部分:

    • 不使用身份敏感操作,例如实例之间的引用相等 (==)、实例的身份哈希码或实例的内在锁同步

    • 没有可访问的构造函数,而是通过不承诺返回实例身份的工厂方法实例化;

    正如 Brian Goetz 在 2015 年 1 月 6 日评论的那样:

    Optional 是新的,免责声明在第一天就到了。另一方面,Integer 可能已经无可救药地被污染了,我相信如果 Integer 不再存在,它会破坏大量重要代码可锁定(尽管我们可能会想到这种做法。)


    参考:Value-Based Classes // nipafx

    【讨论】:

    • 好引用。我想,“无可救药的污染”正是我在回答中所描述的……
    • 仅供参考,据我所知,当前的值类型愿景仍然能够包含 Integer
    猜你喜欢
    • 1970-01-01
    • 2013-05-18
    • 1970-01-01
    • 1970-01-01
    • 2016-08-19
    • 2018-08-14
    • 1970-01-01
    • 1970-01-01
    • 2021-06-20
    相关资源
    最近更新 更多