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

2022-09-04 22:45:34

所以我遇到了一个关于按位运算符和位移位的奇怪行为。我试图通过使用位掩码更快地进行小检查,我遇到了这个:

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, ,并且不给出编译错误;但是, 和 做。编译器要求显式转换为 或将最后一个变量声明为 。我注意到比特瓦兹和也有类似的行为。bchdefgbyteint&|

有人能解释一下这里发生了什么吗?编译器为 、 和 工作工作具有什么样的魔力?abch

编辑:或者这如何不完全是重复的

我相信这个问题是不同的,因为为什么我不能添加两个字节并得到一个int,我可以添加两个最后一个字节得到一个字节?因为导致有趣的行为的是编译器如何优化bitwize移位操作。

由于我寻求一个理论上的答案(因为我已经明白我可以通过强制转换来编译我的代码)来决定移位和其他bitwize操作如何确定它们的返回值,我相信这个问题可以补充Java - 位移位与整数和字节,并为StackOverflow带来更多有趣的信息。


答案 1

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

提升的类型是 - 这就是为什么在大多数情况下,您必须按如下方式投射结果的原因:byteint

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

所以真正的问题是,为什么在前3行上,不需要选角。现在,如果您要更改该行,则两个原因都不准确:

private final static int constant = 3;

自:

private final static int constant = 1000;

您将在以下位置收到编译错误:

byte c = 0b1 << constant;

也。

shift 操作可能会创建一个整数,其值大于赋值左侧的字节可以容纳的值 - 这会触发编译时错误并强制我们转换为字节,以便仅获取最低有效 8 位。

那么为什么在前3行中我们不需要转换为字节呢?
编译器认识到我们正在使用一个常量(或 final),因此“知道”此值以后无法更改,因此它允许将赋值缩小到字节的基元转换 - 在左侧:

byte c = 0b1 << 3;

答案 2

简短的回答:

编译器知道值或 a 不能更改,并且可以安全地隐式和具有给定值的 a。finalliteralcastconstant3byte

不能以同样的方式推理非最终值。

显式比隐式更好。

这是为什么我讨厌与编写程序相关的隐含内容的一个例子。

锻炼:

将文字或文字更改为不适合的内容,看看它如何抱怨constant3byte


推荐