在Java中将字节转换为整数的最优雅方法是什么?

27

示例代码:

int a = 255;
byte b = (byte) a;
int c = b & 0xff; // Here be dragons
System.out.println(a);
System.out.println(b);
System.out.println(c);

我们从整数值255开始,将其转换为一个字节(变成-1),然后使用一个神奇的公式将其转换回int。预期输出结果为:

255
-1
255
我想知道这个 a & 0xff 是否是最优雅的转换方式。例如,checkstyle 就会抱怨在这个位置使用魔法数字并且不建议忽略该值进行此检查,因为在其他地方255可能真的是应该避免使用的魔法数字。而且自己定义一个常量来处理这样的东西也很烦人。所以我想知道是否有标准方法在JRE中可以代替这种转换?或者已经定义了具有最高无符号字节值的常量(类似于Byte.MAX_VALUE是最高有符号值)?

因此,为了让问题简短:如何将字节转换为整数而不使用魔法数字?

好吧,到目前为止提到了以下几种可能性:

  • 继续使用& 0xff并忽略checkstyle中的魔法数字255。缺点:其他可能在一些其他范围(而非位运算)使用该数字的地方也将不进行检查。优点:简短易读。
  • 为其定义自己的常量,然后使用代码& SomeConsts.MAX_UNSIGNED_BYTE_VALUE。缺点:如果我需要在不同的类中使用它,则必须定义自己的常量类。优点:这里没有魔法数字。
  • 进行一些聪明的数学计算,例如b& ((1 << Byte.SIZE) - 1)。编译器输出最可能相同,因为它被优化为一个常量值。缺点:代码很长,难以阅读。优点是:只要1未被定义为魔法数字(checkstyle默认忽略它),我们就没有魔法数字,也不需要定义自定义常量。而且如果有一天重新定义字节为16位(开玩笑),那么它仍然有效,因为Byte.SIZE将变为16而不是8。

还有更多想法吗?也许会有其他比上述方法更短并且只使用0和1之类的数字的聪明位运算吗?


我不太确定你真正想在这里做什么,或者说,为什么要这样做。警告:不要混合字节和字符!一个字节只能容纳7位ASCII字符,而不是Java字符! Unicode字符宽度为21位,而不仅仅是7位,甚至不是15/16位。在大多数情况下,Java内部将字符表示为可变宽度(即1或2)的UTF-16代码单元(例外:java.unit.regex必须使用UTF-32进行模式匹配)。你不能合理地屏蔽/保存21位数量中的低7位,然后留下任何明智和有意义的东西。 - tchrist
@tchrist:我这里从未提到字符。只是字节。8位字节。没有多余的,也没有少的。 - kayahr
好的,但是要使用有符号的8位数。 - tchrist
1
@tchrist:不,我说的是UNSIGNED字节。我知道Java字节是有符号的(这在Java中是非常令人烦恼的事实。很遗憾,“unsigned”关键字作为Oak规范的一部分从未被实现...)所以这个话题是关于将无符号字节转换为int。对于有符号字节,我不需要任何魔法。 - kayahr
5个回答

18

这是进行该转换的标准方式。如果您想要摆脱checkstyle的投诉,请尝试定义一个常量,它可能会有所帮助:

 public final static int MASK = 0xff;

顺便提一下,这仍然是一种自定义转换。 byte 是一种有符号数据类型,因此 byte 永远无法存储值 255。 一个字节可以存储位模式 1111 1111,但这表示整数值 -1

实际上,你正在进行位运算-并且位运算总是需要一些神奇的数字。


顺便说一下:是的,有一个Byte.MAX_VALUE常量,但是由于byte是有符号的,它被定义为2的7次幂减1(= 127)。因此在你的情况下不起作用。你需要一个值为-1的字节常量。


看起来没有更多的想法了。所以我接受得票最高的那一个。谢谢大家的意见。 - kayahr

10

忽略checkstyle。 0xFF 不是一个魔数。如果你为它定义一个常量,那么这个常量就是一个魔数,比起 0xFF 本身来说要不易懂得多。如果任何程序员在近几个世纪受过教育,他应该比了解女友更加熟悉 0xFF

我们应该这样编写代码吗?

for(int i = Math.ZERO; ... )

5

Java 8提供了Byte.toUnsignedIntByte.toUnsignedLong(可能用于非常大的字节)方法:

byte b = (byte)255;
int c = Byte.toUnsignedInt(b); // 255
long asLong = Byte.toUnsignedLong(b); // 255

3

2
我写了一个这样的方法:
public static int unsigned(byte x) {
    return int (x & 0xFF);
}

这个函数也可以重载为short和int参数(其中int会扩展为long)。

你可以使用Byte.MAX_VALUE + Byte.MAX_VALUE + 1代替0xFF,以避免FindBug的警告,但我认为这是一种混淆。而且很容易出错(参见以前的版本)。


1
Byte.MAX_VALUE+Byte.MAX_VALUE是254而不是255。我本可以使用(Byte.MAX_VALUE << 1) + 1代替(1已经被checkstyle定义为非魔法数字),但是...好吧...不用 :-) - kayahr
谢谢更正,实际上,像Byte.ALL_ONES这样的常量会很好。 - maaartinus
2
@Paŭlo Ebermann:不起作用。魔术技巧只有在您将字节与整数(0x000000ff)进行AND操作时才起作用。因此,当您使用两个字节进行AND操作时,结果仍然是一个字节,然后将其转换为int,这就是0xff转换为0xffffffff(-1)的地方。 - kayahr

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