Widening Primitive Conversion & Narrowing Primitive Conversion

来源:互联网 发布:卖淘宝店铺有什么风险 编辑:程序博客网 时间:2024/06/05 03:09

这几天看《Java Puzzlers》里面好些题目都是关于byte,char,int float,double的一些隐式和显示的类型转换问题,今天仔细看了一下JLS里面关于类型转换的问题.


Widening Primitive Conversion

主要是小范围类型向大范围类型进行转换,主要有19种
  • byte to shortintlongfloat, or double

  • short to intlongfloat, or double

  • char to intlongfloat, or double

  • int to longfloat, or double

  • long to float or double

  • float to double

以下几种宽类型转换是不会丢失信息的,(大范围类型的精度足够存放小范围类型的信息)
  • from an integral type to another integral type

  • from byteshort, or char to a floating point type

  • from int to double

  • from float to double in a strictfp expression 

以下几种转换是会丢失信息的:int to float, long to float, float to double

转换规则如下:
二进制补码的符号位扩展(char是两字节的无符号类型,所以是0扩展)


Narrowing Primitive Conversion

主要是大范围类型向小范围类型进行转换,主要有22种:
  • short to byte or char

  • char to byte or short

  • int to byteshort, or char

  • long to byteshortchar, or int

  • float to byteshortcharint, or long

  • double to byteshortcharintlong, or float

窄型类型转换是可能出现值的大小和精度的变化(lose information)

当整型S向整型D(大小是n个字节)进行窄型转化时,最终结果是截取整型S的低N个字节
int i = -1;   // i = 0xffffffffshort s = (short)i; //s = 0xffff

当浮点型S向整形D进行转换时,遵循规则如下:

1. 当D是int or long:
   1) 当S是NAN,D = 0
   2)其他情况,如果D能表示S,则D = S,若不能表示D为D的最小值(当S比较小的时候),D为它的最大值(当S比较大的时候)
2. 当D是byte ,char, short:
   先将D看成int or long,然后将得到的结果窄型转换成byte,char,short

针对浮点型向整型我们看一个例子:
public static void testNarrowPrimitiveConversion() {System.out.println("===============NarrowPrimitiveConversion===============");float fmin = Float.NEGATIVE_INFINITY;float fmax = Float.POSITIVE_INFINITY;System.out.println("long: " + (long) fmin + ".." + (long) fmax);System.out.println("int: " + (int) fmin + ".." + (int) fmax);System.out.println("short: " + (short) fmin + ".." + (short) fmax);System.out.println("char: " + (int) (char) fmin + ".."+ (int) (char) fmax);System.out.println("byte: " + (byte) fmin + ".." + (byte) fmax);}

输出结果如下:

===============NarrowPrimitiveConversion===============long: -9223372036854775808..9223372036854775807int: -2147483648..2147483647short: 0..-1char: 0..65535byte: 0..-1


从结果可以看到float转换成long, int ,char都是它们对应类型的最大值和最小值,而对于short和byte的结果,可以这么理解,先转换成int的结果,在进行窄化类型转换。
转换成int的结果是 Tmin = 8000 0000 Tmax = 7fff ffff,转换成short和byte都是取低位两个字节为0x0000和0xffff,所以结果如上所示.

整合宽型转换和窄型转换,在来看个例子,它是《Java Puzzlers》的第六个问题:
public static void main(String[] args) {System.out.println((byte) -1);System.out.println((int)(char)(byte) -1);}


程序第一行:-1是个int型字面量,4个字节,0xffffffff,窄型转换成byte,截取低位1个字节为0xff,输出-1。
程序第二行:(byte)-1的结果是0xff,符号扩展成char:0xffff,在转换成int,因为char是无符号类型,是0扩展的,所以最终结果是0x0000 ffff,输出是65535


在说一下基本数据类型在算术运算中的类型转换:
单目操作运算符:
  • 如果操作数的类型是ByteShortCharacter, or Integer,首先回进行unbox conversion,然后转换成int
  • 如果操作数的类型是Long,Double,Long,直接进行unbox conversion
  • 如果是byte,short,char,则直接转换成int
单目操作运算符有如下几个:创建数组的表达式,数组选择,正负符号,位取反,左移,右移等,JLS里面有个解释非常好的例子如下所示:
class Test {    public static void main(String[] args) {        byte b = 2;        int a[] = new int[b];  // dimension expression promotion        char c = '\u0001';        a[c] = 1;              // index expression promotion        a[0] = -c;             // unary - promotion        System.out.println("a: " + a[0] + "," + a[1]);        b = -1;        int i = ~b;            // bitwise complement promotion        System.out.println("~0x" + Integer.toHexString(b)                           + "==0x" + Integer.toHexString(i));        i = b << 4L;           // shift promotion (left operand)        System.out.println("0x" + Integer.toHexString(b)                           + "<<4L==0x" + Integer.toHexString(i));    }}

对于双目运算符:
基本数据类型的转换如下:
  • 如果操作数是引用类型,直接进行unbox conversion
  • 如果两个操作数中有double,则另外一个也转换成double
  • 如果两个操作数中有float,则另外一个也转换成float
  • 如果两个操作数中有long,则另外一个也转换成long
  • 其他情况都两个操作数转换成int
双目运算符主要包括算术运算,逻辑运算,关系运算,在贴一个JLS的例子
class Test {    public static void main(String[] args) {        int i    = 0;        float f  = 1.0f;        double d = 2.0;        // First int*float is promoted to float*float, then        // float==double is promoted to double==double:        if (i * f == d) System.out.println("oops");        // A char&byte is promoted to int&int:        byte b = 0x1f;        char c = 'G';        int control = c & b;        System.out.println(Integer.toHexString(control));        // Here int:float is promoted to float:float:        f = (b==0) ? i : 4.0f;        System.out.println(1.0/f);    }}

这里要提的是在JLS中有一句话是这么说的In certain cases, the conditional operator ? : 也使用双目运算符的转换规则,这让我想起了《Java Puzzlers》的第八个题目
public static void main(String[] args) {char x = 'X';//final int i = 0;int i = 0;System.out.println(true ? x : 0);System.out.println(false ? i : x);}

如果按照双目运算符的规则来看的话,因为0,和i都是int,那么x会转换成int,所以输出应该是两个88,但是结果不是的!!!第一个输出的X,第二个输出的是88

书中给出的解释是这样的:
1.如果第二个和第三个操作数具有相同的类型,那么它就是条件表达式的类型。换句话说,你可以通过绕过混合类型的计算来避免大麻烦。
2.如果一个操作数的类型是 T,T 表示 byte、short 或 char,而另一个操作数是一个 int 类型的常量表达式,它的值是可以用类型T表示的,那么条
件表达式的类型就是 T。(这就是为什么第一个输出是X的缘故,0是常量表达式,所以输出是System.out.println(char))
3.否则,将对操作数类型运用二进制数字提升(就是双目运算符的转换规则),而条件表达式的类型就是第二个和第三个操作数被提升之后的类型。

然后我看了一下JLS对条件表达式的官方说明,上述3条规则只是一个子集而已,但是对于一般的问题上述规则是可以解决的!

OK!今天就这样吧,类型转换的知识肯定不止这一点,以后遇到在补上吧!


0 0