"可能的有损转换"是什么意思,我该如何解决?

Ste*_*n C 7 java compiler-errors

新的Java程序员经常对编译错误消息感到困惑,例如:

"不兼容的类型:从double到int的可能有损转换"

对于这行代码:

int squareRoot = Math.sqrt(i);
Run Code Online (Sandbox Code Playgroud)

这个错误意味着什么,你如何解决它?

Ste*_*n C 17

首先,这是一个编译错误.如果你在运行时在异常消息中看到它,那是因为你运行了一个带有编译错误的程序1.

消息的一般形式是:

"不兼容的类型:可能有损转换<type1><type2>"

where <type1><type2>都是原始数字类型; 的即,一个byte,char,short,int,long,floatdouble.

当你的代码试图做一个发生这种错误的隐式从转换<type1><type2>但转换可能是有损耗的.

在问题中的示例中:

  int squareRoot = Math.sqrt(i);
Run Code Online (Sandbox Code Playgroud)

sqrt方法产生double,而是从一个转换doubleint具有潜在的损耗.

"潜在有损"是什么意思?

那么让我们看看几个例子.

  1. 的A转换long到一个int是一个潜在的有损转换,因为有long不具有一个相应的值int的值.例如,任何long大于2 ^ 31 - 1的值都太大而无法表示为int.同样,任何小于-2 ^ 31的数字都太小.

  2. a int到a的long转换不是有损转换,因为每个int值都有相应的long值.

  3. 的A转换float到一个long是一个潜在的有损转换,因为有float值过大或过小,无法表示为long数值.

  4. a long到a的float转换不是有损转换,因为每个long值都有相应的float值.(转换后的值可能不太精确,但"损失"并不意味着......在这种情况下.)

这些都是可能有损的转换:

  • shortbytechar
  • charbyteshort
  • intbyte,shortchar
  • longbyte,short,charint
  • floatbyte,short,char,int或者long
  • doublebyte,short,char,int,longfloat.

你如何解决这个错误?

使编译错误消失的方法是添加类型转换.例如;

  int i = 47;
  int squareRoot = Math.sqrt(i);         // compilation error!
Run Code Online (Sandbox Code Playgroud)

  int i = 47;
  int squareRoot = (int) Math.sqrt(i);   // no compilation error
Run Code Online (Sandbox Code Playgroud)

但这真的是一个解决方案吗?考虑平方根476.8556546004......但squareRoot会得到值6.(转换将截断,而不是舍入.)

那怎么样?

  byte b = (int) 512;
Run Code Online (Sandbox Code Playgroud)

这导致b获得价值0.通过掩蔽高阶位来完成从较大的int类型到较小的int类型的转换,并且低阶8位512全部为零.

简而言之,您不应该简单地添加类型转换,因为它可能无法为您的应用程序执行正确的操作.

相反,您需要了解代码需要进行转换的原因:

  • 这是因为您在代码中犯了其他错误吗?
  • 应该<type1>是不同的类型,这样就不需要有损转换吗?
  • 如果转换是必要的,那么类型转换会做出正确行为的静默有损转换吗?
  • 或者您的代码是否应该进行一些范围检查并通过抛出异常来处理错误/意外值?

一些具体案例:

下标时"可能有损转换".

第一个例子:

for (double d = 0; d < 10.0; d += 1.0) {
    System.out.println(array[d]);  // <<-- possible lossy conversion
}
Run Code Online (Sandbox Code Playgroud)

这里的问题是数组索引值必须是int.所以d必须转换doubleint.通常,使用浮点值作为索引是没有意义的.有人认为Java数组的工作方式与(比如)Python字典相似,或者他们忽略了浮点运算通常不精确的事实.

解决方案是重写代码以避免将浮点值用作数组索引.(添加类型转换可能是一个不正确的解决方案.)

第二个例子:

for (long l = 0; l < 10; l++) {
    System.out.println(array[l]);  // <<-- possible lossy conversion
}
Run Code Online (Sandbox Code Playgroud)

这是前一个问题的变体,解决方案是相同的.不同之处在于根本原因是Java数组仅限于32位索引.如果你想要一个"数组类似"的数据结构,它有超过2 31 1个元素,你需要定义或找到一个类来完成它.


1 - 例如,Eclipse IDE有一个选项,允许您忽略编译错误并运行代码.如果选择此选项,IDE的编译器将创建一个21文件,其中带有错误的方法将在调用时抛出未经检查的异常.异常消息将提及编译错误消息.