Integer -> double unbox,但 Double -> int 不拆箱。为什么?

Integer -> double unboxes, but Double -> int doesn't unbox. Why?

提问人:Vince 提问时间:10/14/2023 最后编辑:Vince 更新时间:10/19/2023 访问量:184

问:

为避免混淆,我正在寻找规则/JLS 条目。

  • 我不是在问为什么会失败,我是在问它失败的方式Double -> int

  • 正如我在问题中提到的,我知道有损转换 - 我不是在问数据丢失double -> int

  • 我不是在问某人为什么开发人员以这种方式设计它的“最佳猜测”

我问为什么执行转换(拆箱和加宽),而不执行转换(甚至不拆箱)Integer -> doubleDouble -> int

我正在寻找提到这种转换不一致的 JLS 条目,其中取消装箱发生在一种情况下,而不是另一种情况下。Reference -> primitive


此转换编译时没有错误Integer -> double

double d = Integer.valueOf(1);

这意味着会发生以下情况:

  1. Integer已取消装箱int
  2. 该值经历了从intint -> double

Integer已拆箱。然后,将扩大未装箱的值。这给出了与int -> double


创建的假设也将拆箱,其行为与Double -> intdouble -> int

对于代码

int i = Double.valueOf(1);

我期待错误消息

从 double 到 int 的有损转换

假设 gets unboxed,我们应该观察到与Doubledouble -> int

相反,我们得到一个输入错误

无法将 Double 转换为 int


这种行为背后的解释是什么?

为什么 之间发生拆箱,但 之间没有拆箱?Integer -> doubleDouble -> int

为什么这些是一致的:

  • Integer -> double
  • int -> double

但这些不是:

  • Double -> int
  • double -> int
Java 数据转换 基元 装箱

评论

3赞 Mr. Polywhirl 10/14/2023
因为将整数“升级”为浮点数更容易。如果你反其道而行之,你就会牺牲精确度。正如您所描述的,这是一个有损转换。
0赞 Vince 10/14/2023
@Mr.Polywhirl 那么,为什么我们没有像预期的那样收到有损转换消息呢?为什么是类型错误?为什么拆箱发生在 ,但似乎不会发生在 ?这就是我要问的问题Integer -> doubleDouble -> int
2赞 Louis Wasserman 10/14/2023
如果它知道它仍然会在另一端出现类型错误,为什么还要尝试拆箱?
0赞 Joop Eggen 10/14/2023
double-to-int 和 Double-to-int 是有损转换。最初只有一个双向 int 给出类似 C++ 的警告。但如今,简单的演员阵容是预期的风格。因此,对于 Double-to-int,没有做出任何努力来允许这种不明确和间接的两步转换。当然,我没有参与语言设计,但需要隐式地(int)(double)来做。(int)
0赞 Jorn 10/14/2023
“他们为什么要这样设计”——问题是基于意见的,而且偏离了主题

答:

2赞 Mike 'Pomax' Kamermans 10/14/2023 #1

Double只有两种方法:一种用于字符串输入,一种用于双精度输入valueOf

因此,当您写下这显然是 ,方法签名是这样的(如果它甚至要运行),您的值将升级为 a,然后方法运行。因此,Java 编译器真的会说 your 是“double不能安全地转换为int,所以:不”。Double.valueOf(1)1intvalueOf(double)intdoubleint i = Double.valueOf(1)int i = Double.valueOf(1.0)

当然,它甚至不需要这样做:它知道 的返回值是 ,无论你输入了什么,所以它看到试图将 Double 分配给 int 的代码,知道这是不可能的,并且不关心其他任何事情,它会说“RHS 与 LHS 不兼容,所以:不”。Double.valueOfDouble

有趣的是,“The[sic] create the assumpsion[sic] that Double -> int will also unbox”既是错误的(没有值的拆箱发生),也不是完全错误的(有“unboxing”类型)。你给Java一个带有类型的赋值,Java看到不兼容的类型,但也知道这是一个原始赋值,可以拆箱到基元。然而,由于这仍然是一个不兼容的赋值,Java 会忽略原始的 int/Double 不兼容错误(这样你就知道“你的代码出了什么问题”,而不是“解析器应用的任何代码魔术中出了什么问题”)int = DoubleDoubledouble

评论

0赞 Reilas 10/14/2023
一些有效点,+1
0赞 Vince 10/16/2023
我没有假设接受了.我本来可以写的.你一直在说话,就好像你是编译器一样,或者通过编译器的眼睛看,比如“Java 看到了不兼容的类型”和“RHS 与 LHS 不兼容,所以:不”,但我不是在寻找某人对它的看法,而是来自开发人员自己的解释行为的片段。您是否有来自 JLS 的参考资料来解释该行为?Double.valueOf(1)intDouble.valueOf(...)
0赞 Mike 'Pomax' Kamermans 10/16/2023
文档。例如,docs.oracle.com/javase/specs/jls/se7/html/jls-5.html 和所有其他的解释比任何人都更详细地解释了编译器和 JVM 是如何工作的。Java 的文档非常详尽。
0赞 Vince 10/18/2023
@Mike'Pomax'Kamermans,我知道,我要求涵盖此规则的部分
0赞 Mike 'Pomax' Kamermans 10/18/2023
瞧,阅读文档会让你找到它。别客气。
0赞 Reilas 10/14/2023 #2

"...这给出了与 int -> double ...“ 相同的行为

这就是转换。Integer 对象被取消装箱int
从来没有一个整数可以加倍

"...假设 Double 被拆箱,我们应该观察到与 double -> int 相同的行为......”

我完全不期望 Java 这样做。
而且,不幸的是,我没有指标。

我想,如果该值不能在单个操作中取消装箱和赋值,编译器就会失败。

0赞 meriton 10/16/2023 #3

为避免混淆,我正在寻找规则/JLS 条目。

  • 我不是在问为什么 Double -> int 会失败,我是在问它失败的方式

你要求的东西不存在。

Java 语言规范通常不强制要求编译错误的措辞。特别是,Java 语言规范的相关部分仅描述了赋值上下文中允许哪些隐式转换,但没有列举不允许的转换组合,更不用说在每种情况下应该给出哪个错误消息(或者如果几种情况适用,如何选择消息)。

所有规范的要求是编译器拒绝无效的转换,消息的措辞取决于编译器实现者,不同的编译器实际上发出不同的消息。

例如,如果您要求 Eclipse Compiler for Java 进行编译

int i = Math.PI;
int j = Double.valueOf(Math.PI);

它说:

Type mismatch: cannot convert from double to int
Type mismatch: cannot convert from Double to int

评论

0赞 Vince 10/18/2023
“它不存在”,这不是真的。它是在转换上下文下,Discord 上的某个人帮我找到了它。docs.oracle.com/javase/specs/jls/se21/html/jls-5.html#jls-5.5
0赞 meriton 10/18/2023
正如您的链接所说:“强制转换上下文允许将强制转换表达式 (§15.16) 的操作数转换为由强制转换运算符显式命名的类型。与赋值上下文和调用上下文相比,强制转换上下文允许使用§5.1中定义的更多转换,并允许这些转换的更多组合。
0赞 meriton 10/18/2023
也就是说,该表描述了如何使用显式类型转换的代码进行类型检查。您的代码不使用强制转换运算符,因此该表不适用。
0赞 Vince 10/18/2023
我理解对“强制转换”的混淆,但如果是这种情况,如果表格仅包含显式强制转换,则需要显式强制转换。它还包括隐式强制转换。它定义了显式或隐式转换的规则Object obj = 5;
0赞 Vince 10/18/2023 #4

下表显示了 §5.5 中允许的转换:

  • 表示不允许转换
  • ω 表示扩大基元转换
  • ⊗表示拆箱转换

enter image description here

该表显示了执行取消装箱和扩大转换,但不执行取消装箱。Integer -> doubleDouble -> int

由于不会发生拆箱,因此我们得到的是类型错误,而不是有损转换错误。

评论

0赞 meriton 10/18/2023
该表显示了“强制转换上下文”中允许的转换。例如,此表显示不编译。但是您的代码不使用强制转换运算符,而是使用普通赋值,即 ,其适用不同的规则。(int) Double.valueOf(1)int i = Double.valueOf(1)
0赞 Vince 10/18/2023
@meriton它不需要强制转换运算符,因为存在隐式强制转换