Java 中的基本类型‘ short’-cast

我有一个关于 Java 中基本类型 short的问题,我使用的是 JDK 1.6。

如果我有以下资料:

short a = 2;
short b = 3;
short c = a + b;

编译器不想编译——它说它“不能从 int 转换为 short”,并建议我转换为 short,因此:

short c = (short) (a + b);

真的有用。但我的问题是,我为什么需要选角?A 和 b 的值在 short的范围内-短值的范围是{-32,768,32767}。 当我想执行-,* ,/(我还没有检查其他的)操作时,我也需要强制转换。

如果对基元类型 int执行相同的操作,则不需要将 aa + bb 强制转换为 int。以下工作良好:

int aa = 2;
int bb = 3;
int cc = aa +bb;

我是在设计一个类时发现这个问题的,我需要添加两个 short 类型的变量,编译器希望我进行强制转换。如果使用 int类型的两个变量执行此操作,则不需要强制转换。

一个小注意: 同样的事情也发生在原语类型 byte上:

byte a = 2;
byte b = 3;
byte c = (byte) (a + b);

但这不是:

byte a = 2;
byte b = 3;
byte c = a + b;

对于 longfloatdoubleint,不需要强制转换,只需要对 shortbyte值进行强制转换。

137483 次浏览

你用什么语言?

许多基于 C 的语言都有一个规则,即任何数学表达式的大小都是 int 或更大。因此,一旦添加了两个 short,结果就是 int 类型的。这就需要石膏了。

在 C # 和 Java 中,赋值右边的算术表达式在默认情况下计算为 int。这就是为什么需要转换回 short,因为出于显而易见的原因,没有 int 到 short 的隐式转换形式。

正如在 短 C # 中所解释的那样(也适用于其他语言编译器,如 Java)

有一个预定义的隐式转换,从 short 转换为 int、 long、 float、 double 或 decal。

不能隐式地将存储大小较大的非文字数值类型转换为较短的数值类型(有关整数类型的存储大小,请参阅整数类型表)。例如,考虑以下两个短变量 x 和 y:

short x = 5, y = 12;

下面的赋值语句将产生编译错误 因为赋值运算符右侧的算术表达式在默认情况下计算结果为 int。

short z = x + y;   // Error: no conversion from int to short

要解决此问题,请使用强制转换:

short z = (short)(x + y);   // OK: explicit conversion

可以使用下列语句,其中目标变量具有相同的存储大小或更大的存储大小:

int m = x + y;
long n = x + y;

一个很好的后续问题是:

“为什么赋值运算符右边的算术表达式在默认情况下计算结果为 int”?

第一个答案是:

对整数常数折叠进行分类和正式验证

Java 语言规范确切地定义了如何表示整数数字以及如何计算整数算术表达式.这是 Java 的一个重要特性,因为这种编程语言被设计用于 Internet 上的分布式应用程序。Java 程序需要独立于执行它的目标机器产生相同的结果.

相比之下,C (和大多数广泛使用的命令式和 面向对象程序设计语言)更为草率,留下了许多重要特征 同样的 C 程序应该在16位上运行, 32位,甚至可以通过实例化64位元的整数算法 目标处理器中内置算术运算的源程序。这将导致更高效的代码,因为它可以使用可用的 机器操作直接。只要整数计算只处理 如果数字“足够小”,就不会产生矛盾。

从这个意义上说,C 整数算法是一个占位符,它没有被精确定义 但是只有通过确定目标机器才能完全实例化。

Java 精确地定义了如何表示整数以及如何计算整数算术。

      Java Integers
--------------------------
Signed         |  Unsigned
--------------------------
long  (64-bit) |
int   (32-bit) |
short (16-bit) |  char (16-bit)
byte  (8-bit)  |

Char 是唯一的无符号整数类型。它的值表示 Unicode 字符,从 \u0000\uffff,即从0到216-1。

如果整数运算符具有 long 类型的操作数,则另一个操作数也会转换为 long 类型。否则,对 int 类型的操作数执行操作,如有必要,较短的操作数将转换为 int 。正确地指定了转换规则。

[摘自《理论计算机科学电子笔记》82卷第2期(2003)
布莱斯纳-布莱奇-COCV 2003: Sabine GLESNER,简 · 奥拉夫 · 布莱奇,
信息学院,
卡尔斯鲁厄理工学院
德国,卡尔斯鲁厄

编辑: 好的,现在我们知道它是 Java..。

Java 语言规范 第4.2.2节规定:

Java 编程语言提供 许多操作员的行为 整数值:

[...]

  • 数值运算符,结果是 在 int 或 long 类型的值中:
  • [ ... ]
  • < li > 加法运算符 + 和 - (15.18)

    换句话说,它类似于 C #-加法运算符(当应用于整数类型时)只能得到 intlong,这就是为什么需要强制转换来赋值给 short变量。

    原答案(C #)

    在 C # 中(您还没有指定语言,所以我猜测) ,基元类型上唯一的加法运算符是:

    int operator +(int x, int y);
    uint operator +(uint x, uint y);
    long operator +(long x, long y);
    ulong operator +(ulong x, ulong y);
    float operator +(float x, float y);
    double operator +(double x, double y);
    

    这些都在 C # 3.0规范的7.7.4部分中。另外,十进制加法定义如下:

    decimal operator +(decimal x, decimal y);
    

    (此处还定义了枚举添加、字符串串联和委托组合。)

    如您所见,没有 short operator +(short x, short y)操作符-因此两个操作数都隐式转换为 int,并且使用 int 格式。这意味着结果是“ int”类型的表达式,因此需要强制转换。

    鉴于“为什么默认为 int”这个问题还没有得到回答..。

    首先,“违约”实际上不是一个正确的术语(尽管非常接近)。正如 VonC 所指出的,由 int 和 longs 组成的表达式将产生长结果。由 int/log 和 double 组成的操作将产生双重结果。编译器将表达式的术语升级为能够在结果中提供更大范围和/或精度的任何类型(浮点类型被认为具有比整数更大的范围和精度,尽管将大长度转换为双精度会失去精度)。

    需要注意的是,这种促销只发生在需要它的条件下。因此,在下面的示例中,子表达式5/4仅使用整数值,并使用整数计算来执行,尽管整个表达式包含一个 double。结果不是你想的那样。

    (5/4) * 1000.0
    

    那么,为什么 byte 和 short 被升级为 int 呢?没有任何参考来支持我,这是由于实用性: 字节码的数量是有限的。

    字节码,顾名思义,使用一个字节来指定一个操作。例如 Iadd,它添加了两个 int。目前,定义了205个操作码和整数计算对每种类型都采用 18(即,整数和长数之间的总数为36) ,不计算转换运算符。

    如果 short 和 byte 每个都有自己的操作码集,那么您将处于241,这限制了 JVM 扩展的能力。正如我所说,没有参考支持我这一点,但我怀疑高斯林等人说: “多久人们实际上使用短裤?”另一方面,将 byte 升级为 int 会导致这种不那么好的效果(预期的答案是96,实际的答案是 -16) :

    byte x = (byte)0xC0;
    System.out.println(x >> 2);
    

    我想补充一些没有被指出的东西。Java 没有考虑您在... 中给出的变量(2和3)的值。

    短 a = 2; 短 b = 3; Short c = a + b;

    据 Java 所知,你可以这样做。

    空头 a = 32767; 短期 b = 32767; Short c = a + b;

    这将超出 short 的范围,它将结果自动装箱为 int,因为“可能”结果将超过 short 但不超过 int。Int 被选为“默认值”,因为基本上大多数人的硬编码值不会超过2,147,483,647或低于 -2,147,483,648

    在 java 中,每个数字表达式都是这样的:

    anyPrimitive zas = 1;
    anyPrimitive bar = 3;
    ?? x = zas  + bar
    

    X 的结果总是至少是一个 int,或者如果其中一个加法元素是 long,那么 x 的结果总是一个 long。

    但是有些怪癖很难对付

    byte a = 1; // 1 is an int, but it won't compile if you use a variable
    a += 2; // the shortcut works even when 2 is an int
    a++; // the post and pre increment operator work
    

    任何小于“ int”(布尔值除外)的数据类型都隐式转换为“ int”。

    就你而言:

    short a = 2;
    short b = 3;
    short c = a + b;
    

    (a + b)的结果隐式转换为 int。现在你把它分配到“ short”。这样你就得到了错误。

    Short,byte,char ——对于所有这些,我们将得到相同的错误。

    Java 总是使用至少32位的值进行计算。这是由于32位体系结构在1995年引入 Java 时很常见。中央处理器的寄存器大小是32位,算术逻辑单元接受的数字是中央处理器寄存器长度的2倍。因此,CPU 针对这些值进行了优化。

    这就是为什么所有支持算术运算且少于32位的数据类型在您使用它们进行计算时立即转换为 int (32位)的原因。

    因此,总的来说,它主要是由于性能问题,并保持现在的兼容性。

    AFAIS,没有人提到 final的使用。如果修改上一个示例并将变量 a 和 b 定义为 final 变量,则编译器是 我保证,它们的和值5可以赋给 类型为 byte的变量,没有任何精度损失。在这种情况下,编译器是好的 将 a 和 b 的和赋给 c。下面是修改后的代码:

    final byte a = 2;
    final byte b = 3;
    byte c = a + b;
    

    如果两个值具有不同的数据类型,那么 java 将自动将其中一个值提升为两个数据类型中较大的一个。在您的示例中,当与二进制算术运算符一起使用时,byte、 short 和 char 等较小的数据类型将被“提升”为 int。如果两个操作数都不是整型数,那么仍然是这样。

    short x = 10;
    short y = 20;
    short z = x+y // this will be a compiler error. To solve this then casting would be required
    short z = (short)(x+y) // this will return 30
    short z = (short) x+y //this will return a compiler error
    

    记住,强制转换是一元操作符,因此通过将较大的值强制转换为较小的数据类型,实际上是告诉编译器忽略默认行为。