Java中的原始类型'short' - 强制转换

79

我有一个有关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 。以下内容可以正常工作:

<code><code>int aa = 2;
int bb = 3;
int cc = aa +bb;
</code></code>

在设计一个需要添加两个 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类型的变量需要进行强制类型转换。


特别令人烦恼的是当你有一个需要 short 类型参数的函数时,你想在此参数中传递一个常量。你必须将该常量强制转换为 short 类型。例如,将 0 传递给函数的 short 参数会导致错误。这太不可思议了! - Palo
11个回答

65

C#简介所解释的那样(但其他语言编译器也是如此,比如Java),

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

你不能将存储大小较大的非文字数字类型隐式转换为short(请参见整数类型表格以了解整数类型的存储大小)。例如,考虑以下两个short变量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类型。转换规则完全指定。 【来自2003年Karlsruhe, 德国的计算机科学理论电子笔记82 No. 2的Blesner-Blech-COCV 2003:
Sabine GLESNER, Jan Olaf BLECH,
Informatik Faculty,
Karlsruhe大学】

同时,T a, b; a += b 等同于 T a, b; a = (T) (a + b):请注意编译器生成的强制类型转换。 - wchargin

17

编辑:好的,现在我们知道这是Java……

Java语言规范第4.2.2节中指出:

Java编程语言提供了许多作用于整数值的运算符:

[...]

  • 数学运算符,其结果为int类型或long类型:
  • [...]
  • 加和减运算符 + 和 - (§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"的表达式,因此需要进行强制转换。


    你可以添加http://msdn.microsoft.com/en-us/library/aa691375(VS.71).aspx来链接到7.7.4部分。 - VonC
    是的,没有一个简单的超链接版本的C# 3.0规范确实很遗憾。在我看来,MSDN版本太痛苦了:( - Jon Skeet
    我们现在知道这是Java,因此没有uint或ulong。我不记得Java是否为BigInteger和/或BigDecimal重载了运算符+。 - finnw
    还是0吗?加油...+1,你确实提到了规格;)能否看一下我的回答中的后续问题,并查看您对该主题是否有一些见解?(即“为什么默认为int?”) - VonC
    URL已经被Oracle化。http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.2.2 - BaroqueBobcat

    16
    在C#和Java中,赋值语句右侧的算术表达式默认会计算为int类型。这就是为什么你需要将其强制转换回short类型的原因,因为显然没有从int到short的隐式转换。

    8
    鉴于“为什么默认是int”这个问题还没有得到解答……首先,“默认”并不是很准确的术语(尽管足够接近)。正如VonC所指出的那样,由int和long组成的表达式将具有long结果。而由int/logs和doubles组成的操作将具有double结果。编译器将表达式的项提升为任何类型,以便在结果中提供更大的范围和/或精度(浮点类型被认为比整数类型具有更大的范围和精度,尽管将大longs转换为double会失去精度)。
    一个注意点是,这种提升只发生在需要它的项上。因此,在以下示例中,子表达式5/4仅使用整数值,并使用整数数学执行,即使整体表达式涉及double。结果并非您可能期望的那样...
    (5/4) * 1000.0
    

    好的,那么为什么byte和short会晋升为int?没有任何支持我的参考资料,但实际上是由于实用性:bytecode数量有限。

    "Bytecode"顾名思义,使用单个字节指定操作。例如iadd,它将两个整数相加。目前定义了205个操作码,整数运算对于每种类型(即integer和long之间共36个)都需要18个操作码,不包括转换操作符。

    如果short和byte各自拥有自己的操作码集,您将达到241个,这将限制JVM的扩展能力。正如我所说,没有任何参考资料支持我,但我怀疑Gosling等人是否说过“人们实际上多久使用shorts?”另一方面,将byte提升为int会导致这种不太美妙的效果(预期答案为96,实际答案为-16):

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

    预期的答案是48,不是吗? - mafu
    @mafu 是的。所以>>转换为int??然而,效果是Z80中所谓的SRA(算术右移),它将字节的位向右移动1个位置,丢失最右边的一个并复制最左边的一个(因此将有符号字节除以2),而不是SRL(逻辑右移),它在左侧留下一个零位(与将无符号字节除以2相同),这是基于“预期答案”的。 - Heimdall

    5

    您使用的是什么语言?

    许多基于C的语言有一个规则,即任何数学表达式都以int或更大的尺寸执行。因此,一旦您添加了两个shorts,结果就是int类型。这导致需要进行转换。


    2

    Java计算时始终使用至少32位的值。这是由于1995年Java推出时普遍采用的32位架构。CPU中的寄存器大小为32位,算术逻辑单元接受2个与CPU寄存器长度相同的数字。因此,CPU被优化为处理这种值。

    这就是为什么所有支持算术操作且小于32位的数据类型在进行计算时都会转换为int(32位)的原因。

    总之,这主要是由于性能问题,并且现在为了兼容性而保留。


    是的,但这并不是全部真相,显然你可以将16位或8位值存储到16位和8位寄存器中,然后在那里执行算术运算。这将允许拥有更多的寄存器变量,即更少的内存访问,即更高效的代码。当然,Java字节码和JIT编译器必须允许这样做,但这并不意味着这不是Java设计缺陷,阻止编写更高效的代码。 - Palo

    1
    任何低于“int”的数据类型(布尔值除外)都会隐式转换为“int”类型。
    在您的情况下:
    short a = 2;
    short b = 3;
    short c = a + b;
    

    (a+b)的结果会被隐式转换为int类型,现在你正在将它赋值给"short"。因此你会得到错误。
    对于short、byte和char这些类型,我们都会得到相同的错误。

    1

    在Java中,每个数值表达式都像:

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

    x将始终至少是int,如果其中一个加法元素是long,则为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
    

    1
    AFAIS,没有人提到对此的final用法。如果您修改最后一个示例并将变量a和b定义为final变量,则编译器可以确保它们的和值5可以被赋值给类型为byte的变量,而不会失去精度。在这种情况下,编译器可以将a和b的和分配给c。以下是修改后的代码:
    final byte a = 2;
    final byte b = 3;
    byte c = a + b;
    

    不完全正确。final byte a = (byte) (new Random().nextInt(4));Incompatible types再次出现了。这不仅仅是最终性,而且要能够将其编译为适合类型的值。 - LAFK says Reinstate Monica
    向Herbert Schildt提出你的抱怨,这是他的想法。@LIttleAncientForestKami - Soner from The Ottoman Empire
    如果我有一个@snr,尤其是因为甚至Shildt也无法对抗javac。;-)你的话意味着final __确保编译器__,但这可能不足够。如果我们逐字采用您的示例,一切都很好。但是尝试将b = 3替换为b =(byte)(new Random()。nextInt(4))。并且不兼容的类型回来了,a + b需要再次转换。您可能希望将其添加到答案中。 - LAFK says Reinstate Monica

    0
    如果两个值具有不同的数据类型,则Java将自动将其中一个值提升为两个数据类型中较大的那个。在您的情况下,较小的数据类型(如byte、short和char)在与二进制算术运算符一起使用时将被“提升”为int。即使没有操作数是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
    

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


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