基础概念:
- byte 1 byte -128 to 127.
- short 2 bytes -32,768 to 32,767.
- int 4 bytes -2,147,483,648 to 2,147,483,647.
- long 8 bytes -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807.
先上一段代码:
short a = 5; //✅void setNum(short a);setNum(5); //❌ Possible lossy conversion from int to shortstatic final int INT = 9;setNum(INT); //❌ 即使常量也不允许//强制类型转换后可以setNum((short) 5); //✅setNum((short) INT); //✅
直接赋值的时候可以将 int 隐式转化成 short , 但方法调用时且无法,由于 5 是 int 类型, int 类型数值范围大于 short ,可能会造成精度丢失
可以参照 JLS 关于 原始类型缩小转换 和 转换上下文(赋值上下文、调用上下文)相关主题。
根据 JLS , 以下情况 缩小转换 可以发生在赋值上下文内:
:::info
如果原始变量是 byte 、 short 、 char ,并且常量表达式的值是这个变量的类型可表达的数值范围内。
:::
short f = 32768; //❌ 看上面数值范围short s = 32767; //✅
上面的常量 5 在 short 的数值范围内,所有可以发生 原始类型缩小转换, int 转 short 。
但是在方法调用上下文中,这种转换不被允许,这也就是为什么上面 setNum(5) 会失败。
有人可能会问,如果编译器知道我传递的常量是一个 short 数值范围内的值(和赋值语句一样是5),为什么还编译不过。
如果加上额外的数值范围校验逻辑,无疑增加了编译器的负担。在调用上下文中,编译器负责的是类型安全检查,
已经校验了类型,不该再校验值是否被允许。表达式中值是常量的时候,明显可以知道是否在数值范围内,但是在执行上下文并不是如此。
因为无法知道调用时确切值,也未作校验,所以执行上下文不允许 数值缩小的转换操作,避免精度缺失。
//@1short a = 5; //✅short b = a * 5; //❌//@2final short f = 5; //✅short b = f * 5; //✅ 常量表达式编译期就可以确定,被替换为具体指
1中尽管运行时数值在允许范围内,但编译期无法知道。2中常量表达式,编译期就可确定。
:::warning
赋值上下文中常量表达式在数值范围内,可以发生缩小的数值转换,
但执行上下文中编译器只做类型校验,必须类型相互兼容,不做类型缩小转换,即使入参是常量、数字字面量。
:::
