问题描述
JLS §5.6.1 说:
某些运算符将一元数值提升应用于单个操作数,该操作数必须产生数字类型的值:
...
如果操作数是编译时类型的 byte、short 或 char,则通过扩展原始转换将其提升为 int 类型的值
...
以下情况对表达式进行一元数值提升:
...
移位运算符 > 或 >>>
这就解释了为什么这个程序无法编译:
public class xx {
public short twice(short x) {
return x << 1;
}
}
出现此错误:
$ javac xx.java
xx.java:3: error: incompatible types: possible lossy conversion from int to short
return x << 1;
^
1 error
好的。那么为什么这个程序编译成功呢?
public class xx {
public short two() {
return (short)1 << 1;
}
}
表达式 (short)1
是否符合“编译时类型 byte、short 或 char”的条件?
这是我正在使用的:
$ java -version
openjdk version "1.8.0_282"
OpenJDK Runtime Environment (build 1.8.0_282-bre_2021_01_20_16_37-b00)
OpenJDK 64-Bit Server VM (build 25.282-b00,mixed mode)
解决方法
我已经创建了类似于您的示例代码,然后检查了字节码。 代码如下:
package dsa;
public class Shorts {
public static void main( String[] args ) {
short what = twice();
System.out.println(what);
}
public static short twice() {
return 1 << 1;
}
}
以及生成的字节码:
Compiled from "Shorts.java"
public class dsa.Shorts {
public dsa.Shorts();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: invokestatic #7 // Method twice:()S
3: istore_1
4: getstatic #13 // Field java/lang/System.out:Ljava/io/PrintStream;
7: iload_1
8: invokevirtual #19 // Method java/io/PrintStream.println:(I)V
11: return
public static short twice();
Code:
0: iconst_2
1: ireturn
}
请注意,我不是专家,但在我看来,编译器优化函数返回常量整数,然后将其存储在 int 变量中,直到它被打印出来。所以答案是,套用一部著名电影的名言:“没有短篇”。
,这里的演员阵容是一个红鲱鱼;没有它,代码编译也是一样的。重要的是表达式 1 << 1
是一个常量表达式。所以说 JLS (§5.2,强调我的):
赋值上下文允许将表达式的值赋值(第 15.26 节)给变量;表达式的类型必须转换为变量的类型。 [...] 如果表达式是 byte
、short
、char
或 int
类型的常量表达式(第 15.28 节):如果变量的类型是 byte
、short
或 char
,并且常量表达式的值可以在变量的类型中表示,则可以使用缩小原语转换。
请注意,您的表达式出现在 return
语句中,而不是变量赋值中,但是 §14.17 告诉我们该表达式是一个赋值上下文,因此上面引用的第 5.2 节适用。
所以你有一个 short
类型的赋值上下文,在那个上下文中你有常量表达式 1 << 1
,它导致值 2
,它确实可以在类型 { 中表示{1}}。所以这就是它编译的原因。