Java 一元提升、左移运算符和短

问题描述

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 节)给变量;表达式的类型必须转换为变量的类型。 [...] 如果表达式是 byteshortcharint 类型的常量表达式(第 15.28 节):如果变量的类型是 byteshortchar,并且常量表达式的值可以在变量的类型中表示,则可以使用缩小原语转换。

请注意,您的表达式出现在 return 语句中,而不是变量赋值中,但是 §14.17 告诉我们该表达式是一个赋值上下文,因此上面引用的第 5.2 节适用。

所以你有一个 short 类型的赋值上下文,在那个上下文中你有常量表达式 1 << 1,它导致值 2,它确实可以在类型 { 中表示{1}}。所以这就是它编译的原因。