【问题标题】:Primitive type 'short' - casting in Java原始类型 'short' - Java 中的强制转换
【发布时间】:2010-10-03 10:15:33
【问题描述】:

我对 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 值。

【问题讨论】:

  • 特别烦人的是,当你有一个函数以 short 作为参数时,你想在该参数中传递一个常量给它。您必须将该常量类型转换为short。例如,将 0 传递给函数的短参数会产生错误。这是一个丑闻!

标签: java casting primitive short


【解决方案1】:

short C# 中所述(但也适用于其他语言编译器,如 Java)

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

您不能将具有较大存储大小的非文字数字类型隐式转换为 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”?

第一个答案可以在:

Classifying and Formally Verifying Integer Constant Folding

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 No. 2 (2003)
Blesner-Blech-COCV 2003:Sabine GLESNER,Jan Olaf BLECH,
Fakultät für Informatik,
卡尔斯鲁厄大学
德国卡尔斯鲁厄]

【讨论】:

  • 同样T a, b; a += b 等价于T a, b; a = (T) (a + b):注意编译器添加的演员表。
【解决方案2】:

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

Section 4.2.2 of the Java Language Specification 状态:

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”类型的表达式,因此需要强制转换。

    【讨论】:

    • 您可以添加 msdn.microsoft.com/en-us/library/aa691375(VS.71).aspx 以链接到 7.7.4 部分
    • 是的,很遗憾没有 C# 3.0 规范的 simple 超链接版本。 MSDN版本太痛苦了IMO :(
    • 我们现在知道它是 Java,因此没有 uint 或 ulong。我不记得 Java 是否为 BigInteger 和/或 BigDecimal 重载了运算符 +
    • 还是 0 ?!来吧... +1,您确实提到了规格;)您能否在我的回答中查看我的后续问题,看看您是否对该主题有一些见解?(即“为什么默认为 int ? ")
    【解决方案3】:

    在 C# 和 Java 中,赋值右侧的算术表达式默认为 int。这就是为什么您需要转换回一个 short 的原因,因为显然没有将 int 形式隐式转换为 short 的原因。

    【讨论】:

      【解决方案4】:

      鉴于尚未回答“为什么默认为 int”的问题......

      首先,“默认”并不是真正正确的术语(尽管足够接近)。正如 VonC 所指出的,由 int 和 long 组成的表达式将产生 long 结果。由整数/日志和双精度数组成的操作将产生双精度结果。编译器将表达式的项提升为在结果中提供更大范围和/或精度的任何类型(假定浮点类型具有比整数更大的范围和精度,尽管将大长整数转换为双精度确实会丢失精度)。

      需要注意的是,此促销活动仅针对需要它的条款进行。因此,在以下示例中,子表达式 5/4 仅使用整数值并使用整数数学执行,即使整个表达式涉及双精度。结果不是你所期望的......

      (5/4) * 1000.0
      

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

      “字节码”顾名思义,使用单个字节来指定操作。例如 iadd,它添加了两个整数。目前,205 opcodes are defined 和整数数学对每种类型都采用 18(即整数和长整数之间共有 36 个),不包括转换运算符。

      如果很短,并且每个字节都有自己的操作码集,那么您将处于 241,从而限制了 JVM 扩展的能力。正如我所说,没有任何参考资料支持我,但我怀疑 Gosling 等人说“人们实际上多久使用一次短裤?”另一方面,将 byte 提升为 int 会导致这种不那么美妙的效果(预期答案是 96,实际是 -16):

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

      【讨论】:

      • 预期的答案是 48,不是吗?
      • @mafu 是的。所以>> 转换为int??然而,效果是 Z80 中称为 SRA 的内容(算术右移),它将字节的位向右移动 1 位,丢失最右边的位并复制最左边的位(因此将有符号字节除以 2),与SRL(逻辑右移)相反,后者在左侧留下一个零位(与将无符号字节除以 2 相同),这是“预期答案”的基础。
      【解决方案5】:

      你使用什么语言?

      许多基于 C 的语言都有一个规则,即任何数学表达式都以 int 或更大的大小执行。因此,一旦添加了两条短裤,结果就是 int 类型。这导致需要演员表。

      【讨论】:

        【解决方案6】:

        Java 始终使用至少 32 位的值进行计算。这是由于 1995 年引入 java 时常见的 32 位架构。 CPU中的寄存器大小为32位,算术逻辑单元接受2个cpu寄存器长度的数字。因此,CPU 针对这些值进行了优化。

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

        所以总结起来主要是由于性能问题,现在保留是为了兼容性。

        【讨论】:

        • 是的,但这并不完全正确,显然您可以将 16 位或 8 位值存储到 16 位和 8 位寄存器中,然后在那里执行算术运算。这将允许拥有更多的寄存器变量,即更少的内存访问,即更高效的代码。当然,Java 字节码和 JIT 编译器必须允许这样做,但这并不意味着它不是阻止编写更高效代码的 Java 设计缺陷。
        【解决方案7】:

        在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
        

        【讨论】:

          【解决方案8】:

          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 再次抬起丑陋的脑袋。这不仅仅是最终性,它能够将其编译为适合类型的值。
          • 将您的投诉告诉 Herbert Schildt,这是他的主意。 @LIttleAncientForestKami
          • 如果我有一个@snr 我会的,特别是因为什至连 Shildt 都无法帮助对抗 javac。 ;-) 你的话暗示final 保证编译器,这可能还不够。如果我们逐字逐句地举出你的例子,一切都很好。但是尝试用b = (byte)(new Random().nextInt(4)) 替换b = 3。不兼容的类型又回来了, a+b 需要再次强制转换。您可能希望将其添加到您的答案中。
          【解决方案9】:

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

          在你的情况下:

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

          (a+b) 的结果被隐式转换为 int。现在你将它分配给“short”。所以你得到了错误。

          short,byte,char -- 对于所有这些我们都会得到相同的错误。

          【讨论】:

            【解决方案10】:

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

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

            据 Java 所知,您可以这样做...

            短a = 32767; 短 b = 32767; 短 c = a + b;

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

            【讨论】:

            • 废话,因为我可以添加两个int MAXVALUE,它不需要LHS很长。
            【解决方案11】:

            如果两个值具有不同的数据类型,那么 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
            

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2016-11-25
              • 2015-01-18
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2022-11-27
              • 2023-03-28
              • 1970-01-01
              相关资源
              最近更新 更多