Java中位运算符和位移运算符的返回值有趣的行为

13

我发现了一些关于位运算符和位移的奇怪行为。我试图通过使用位掩码来加速一个小检查,然后我遇到了这个:

public class Weirdness {

    private final static int constant = 3;

    private static int notConstant = 3;

    public void stuff() {
        byte a = 0b1 << 3;
        byte b = 0b1 << (int) 3;
        byte c = 0b1 << constant;
        byte d = 0b1 << notConstant; //error
        byte e = 0b1 << getAnInt(); //error
        byte f = 0b1 << getAFinalInt(); //error
        int i = 3;
        byte g = 0b1 << i; //error
        final int j = 3;
        byte h = 0b1 << j;
    }

    public static int getAnInt() {
        return 3;
    }

    public static final int getAFinalInt() {
        return 3;
    }

}

a, b, ch不会导致编译错误;但d, e, fg会。编译器要求显式地将它们强制转换为byte或将最后一个变量声明为int。我也注意到了使用位运算符&|时有类似的行为。

有人能解释一下这里发生了什么吗? 编译器是如何实现对a, b, ch的工作的?

编辑:或者说这并不完全是重复的问题

我认为这个问题与为什么我不能将两个字节相加并得到一个整数,而我可以将两个final字节相加得到一个字节?不同,因为引起有趣行为的原因是编译器如何优化位移操作。

由于我寻求的是一个理论上的答案(因为我已经明白我可以通过强制转换使我的代码编译),解释位移和其他位运算如何确定它们的返回值,因此我认为这个问题可以补充Java - bit shifting with integers and bytes并为StackOverflow带来更多有趣的信息。


我认为Rohit的回答是你要找的。 - user180100
2个回答

5

来自JLS:移位表达式的类型是左操作数的提升类型。

byte 的提升类型是 int - 这就是为什么在大多数情况下,你必须进行以下类型转换的原因:

byte e = (byte) (0b1 << getAnInt()); 

所以真正的问题是为什么在前三行中不需要转换类型。现在,这也不准确,因为如果你改变了这一行:
private final static int constant = 3;

到:

private final static int constant = 1000;

你会在以下代码上收到编译错误:

byte c = 0b1 << constant;

同样。

移位操作可能会创建一个整数,它的值比赋值语句左侧的字节能够容纳的值要大——这将触发编译时错误,并强制我们将其强制转换为字节以仅获得最低有效的 8 个位。

那么为什么在前三行中我们不需要强制转换为字节?
编译器识别出我们使用了一个常量(或 final),因此“知道”该值后续不能更改,因此允许将Narrowing Primitive Conversion用于赋值给字节——在左侧:

byte c = 0b1 << 3;

哇!谢谢!所以当我没有错误时,基本上这些情况实际上是异常而不是相反。好答案。 - le-doude

3

简短回答:

编译器知道final值或literal是不可变的,可以安全地将constant3隐式转换为给定值的byte

final值无法用同样的方式推理。

显式优于隐式。

这就是我讨厌与编写程序相关的任何implicit的例子之一。

练习:

constant或字面值3更改为无法适应byte的内容,并查看它如何抱怨。


如果我没记错的话,Java都是按值传递的。那么为什么对于'e'和'f'不起作用呢?因为它对于'c'是有效的。我认为Java可以遍历这些引用并进行优化。 - le-doude
1
这与“按值传递”或其他类似内容无关,Java通过按值传递来传递“引用”。函数可以返回任何东西,函数上的final仅意味着它不能被覆盖,它对其返回值没有任何影响。Final Word on Final - user177800

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接