猿问

为什么 int 变量在分配给字节变量时会抛出错误,而不是 int 文字?

我最近开始学习Java,不能只理解该语言的一项功能。

当我编写下面的代码时,我没有收到任何错误(而且明智地我不应该收到任何错误!):

byte b = 10 * 2

但是,当我输入以下代码时,编译器会抛出错误:

int i = 10;
byte b = i * 2

当编译器可以执行检查10 * 2以确保它小于 的范围时byte,为什么它不能也执行检查i * 2并查看它是否小于 的范围byte

它与较低级别的位表示有关,还是与内存有关?


哈士奇WWW
浏览 162回答 6
6回答

潇潇雨雨

在 java 中,每当您尝试执行算术表达式时,如果表达式包含任何类型的变量,Java 都会将该表达式的所有元素转换为该表达式中可用的最高数据类型。所以,当您执行 10*2 时,两个操作数都是文字,而不是变量,因此操作数和结果的数据类型也不会自动提升,除非结果超出数据类型的范围,这里是字节,20 来完全低于字节范围。但是当你执行 i*2 时,表达式由变量组成,其中 i 是 int,结果是 20 但是,它的类型将为 int。因为操作数被自动提升为int,也就是说这里的20在表达式求值时会被提升为int,结果将是int,因为两个操作数都是int。而且 int 不能存储在字节中,即使它在其范围内。因为编译器会认为如果将 int 存储在字节中将会丢失值。因此,在这种情况下,您必须强制将其类型转换为字节。byte b = (byte)(i*2);尝试运行这个你会感到惊讶:byte b = 10; b = b * 2;对此的解释还是和上面说的一样。

慕虎7371278

我对任何特定于 Java 的东西都持否定态度,但任何现代编译器都会执行常量折叠来“折叠”完全是常量的表达式。即,10 * 2 折叠为 20,因此编译器将其视为您键入的内容byte b = 20;对于编译器来说,尝试优化变量并不实际。尽管在您提供的示例中,查看和了解它相对简单i,10但如果编译器尝试优化它并知道它是什么i,它就必须维护自己的符号表,并且本质上是一个解释器。由于java是一种预编译语言,这就达不到目的了。阐述:编译器和解释器之间是有区别的。编译器将源代码作为输入,并在幕后编写机器代码。当机器代码运行时,就会执行操作/执行/计算。Java是一种编译语言,因此它的编译器不做太多计算,它只是编写可以在Java虚拟机上运行的机器代码。另一方面,Python 是一种解释性语言。i * 2当您运行 python 程序时,在实际计算之前它不会尝试进行任何类型转换i * 2。现在,有时编译器会尝试变得聪明,并内置“优化”。这意味着他们不是编写执行某些操作的机器代码,而是用更少的指令编写机器代码,因为它知道它将是什么(因此编译器会进行一些计算来实现这一点)。在您的示例中,编译器可以将 10 和 2 相乘,然后只编写一条机器指令来存储该结果,而不是编写存储数字 10、存储数字 2、将它们相乘、然后存储结果的机器指令。当我们引入变量时,编译器就变得更难优化并找出该变量是什么。实际的编译程序(Java 编译器)必须记住 i 现在是一个保存数字 10 的变量。如果我们想优化只是为了知道我们可以将 i * 2 分配给byte,这意味着编译器必须记住每个整数变量,以防它在后面的表达式中分配给一个字节 - 此时它并不真正值得优化,因为编译器花费了额外的计算(额外的编译工作),而这并没有真正带来任何好处。符号表(如上所述)本质上是一个记住变量及其值的表。

函数式编程

有些语言允许编译器有很大的自由度,使其尽可能聪明。但Java不是这样的语言。Java 的目标之一是您可以使用许多不同的编译器来编译您的代码,并且每次都获得相同的结果,这样您就不必担心您的 IDE 和本地命令行编译器以及您的生产构建系统是否都兼容以同样的方式处理你的代码。所以你的编译器拒绝的原因int i = 10;byte b = i * 2;是只有当所有编译器都被要求接受它时它才能接受它;i这意味着规范必须指定编译器在编译时计算出或不计算出 的值的确切条件范围。这将是一个复杂的混乱,每个编译器都必须使其完全正确。因此,规范以相当简单的方式定义常量表达式(请参阅https://docs.oracle.com/javase/specs/jls/se12/html/jls-15.html#jls-15.28),并且只允许当右侧是相关类型范围内的常量表达式时,隐式缩小转换(请参阅https://docs.oracle.com/javase/specs/jls/se12/html/jls-5.html#jls- 5.2)。当然,您可以通过编写强制转换来解决此问题,以便执行显式缩小转换:int i = 10;byte b = (byte)(i * 2);但编译器不会检查你是否在;20范围内 byte你需要自己做。或者,您可以使i常数:final int i = 10;byte b = i * 2;

holdtom

这里i是一个整数, 也是i*2。您将整数变量分配给字节,这是非法的。您可以通过byte显式地将其强制转换来完成此操作。byte b = (byte) (i * 2);Java 不会执行任何值评估来查看它是否适合/不适合目标类型。

小唯快跑啊

重点是:int i = 10;byte b = i * 2真的很“清楚”,不是吗?人们知道它的值b 必须是 20,并且 20 非常适合字节范围。但假设:int i = someMethodCall();byte b = i * 2现在,当你看到那someMethodCall()具尸体时,也许你也能得出同样的结论。但您会同意:仅此一点就使决定是否i*2仍在字节范围内变得更加复杂。长话短说:编译器可以(并且确实)对源代码应用各种分析。例如,尽可能在编译时计算事物。但具体发生的情况取决于 A) 语言规范和 B) 编译器实现。事实是:编写一个编译器非常简单,它只是说:“使用 int 值来初始化字节值是无效的”。编写一个能够自行决定“此处有效”的编译器需要付出相当大的努力。然后,在很多情况下,编译器不知道,但无论如何都会回来抱怨。换句话说:创建 java 的人们选择了一个简单的编译器。这使得编译器更容易实现,但它会导致用户收到这样的错误消息,(理论上)这是可以避免的(对于很多情况,不是全部)。

不负相思意

虽然编译器对这段代码的分析肯定不会超出可以想象的范围,以找出在这种特定情况下计算结果确实适合一个字节,但i仍然被声明为 an int,所以我怀疑编译器将因此应用这些规则对于任何可能的int和 任何可能的int,该作业都不起作用。
随时随地看视频慕课网APP

相关分类

Java
我要回答