除了赋值,还有另外一种类型变换:在表达式中。想要知道原因,往下看。在表达式中,对中间值的精确要求有时超过任何一个操作数的范围。例如,考虑下面的表达式:
byte a = 40;
byte b = 50;
byte c = 100;
int d = a * b / c;
中间项结果a*b 很轻易超过它的任何一个byte 型操作数的范围。为处理这种问题,当分析表达式时,Java 自动提升各个byte 型或short型的操作数到int 型。这意味着子表达式a*b 使用整数而不是字节型来执行。这样,尽管变量a和b都被指定为byte 型,50*40 中间表达式的结果2000 是合法的。
自动类型提升有好处,但它也会引起令人迷惑的编译错误。例如,这个看起来正确的程序却会引起问题:
byte b = 50;
b = b * 2; // Error! Cannot assign an int to a byte!
该程序试图将一个完全合法的byte 型的值50*2 再存储给一个byte 型的变量。但是当表达式求值的时候,操作数被自动地提升为int 型,计算结果也被提升为int 型。这样,表达式的结果现在是int 型,不强制转换它就不能被赋为byte 型。确实如此,在这个非凡的情况下,被赋的值将仍然适合目标类型。
在你理解溢出的后果的情况下,你应该使用一个显式的强制类型转换,例如:
byte b = 50;
b = (byte)(b * 2);
它产生出正确的值100 。
3.10.1 类型提升的约定
除了将byte 型和shorts 型提升到int 型以外,Java 定义了若干适用于表达式的类型提升规则(type promotion rules )。首先,如刚才描述的,所有的byte 型和short型的值被提升到 int 型。其次,假如一个操作数是long 型,整个表达式将被提升到long 型;假如一个操作数是float 型,整个表达式将被提升到float 型;假如有一个操作数是double 型,计算结果就是double 型。
下面的程序表明:在表达式中的每个值是如何被提升以匹配各自二进制运算符的第二个参数:
class Promote {
public static void main(String args[]) {
byte b = 42;
char c = 'a';
short s = 1024;
int i = 50000;
float f = 5.67f;
double d = .1234;
double result = (f * b) + (i / c) - (d * s);
System.out.println((f * b) + " + " + (i / c) + " - " + (d * s));
System.out.println("result = " + result);}}
让我们进一步看看发生在下列程序行的类型提升:
double result = (f * b) + (i / c) - (d * s);
在第一个子表达式f*b 中,变量b被提升为float 类型,该子表达式的结果当然是float 类型。接下来,在子表达式i/c,中,变量c被提升为int 类型,该子表达式的结果当然是int 类型。然后,子表达式d*s 中的变量s被提升为double 类型,该子表达式的结果当然也是double 类型。最后,考虑三个中间值,float 类型,int 类型,和double 类型。float 类型加int 类型的结果是float 类型。然后float 类型减去提升为double 类型的double 类型,该表达式的最后结果是double 类型。