1. 问题
result = exp1 ? value1 : exp2 ? value2 : value3;
上述表达式中result的类型是什么?
2. 解答
- 前置知识:
- 浮点型大于整形,Double大于Float,Long大于Integer,Integer大于Short,Short大于Byte(大于是指不同类型变量计算时返回大的类型)
- 整数默认类型为Integer、浮点数默认类型为Double
- 基本类型和包装类型混用时,包装类型会自动拆箱为基本类型
- 分类讨论:
- 当且仅当value1、value2和value3为相同的包装类型时,返回值类型为包装类型;若不同,根据规则1,返回值类型为相应的基本类型
- char和Character在和其他数值类型的变量运算中,总是隐式的转换为int类型
- 当value1、value2和value3中包含String、boolean、Boolean类型值,且不含reference类型值时,返回值类型为Serializable类型
- 当value1、value2和value3中存在reference类型时,返回值为Object类型
3. 意义
- 自动拆箱时,有可能发生NullPointException,需要谨慎使用三目运算符的嵌套形式:
- 如
flag ? 1 : null
这样的用法是没有问题的,该表达式的返回值是Integer类型 - 而
flag1 ? 1 : flag2 ? 2 : null
,当flag1和flag2均为false时,会发生NullPointException- 因为该表达式的返回值类型为int,是基本类型,而null不是基本类型,所以需要自动拆箱将值赋给返回值,而“
null.intValue()
”当然会导致发生NullPointException - 即使使用Integer类型的变量去接该返回值,同样还是会发生NullPointException,因为该表达式的返回值类型是确定的(int类型),使用Integer类型接返回值时,只不过是隐式的使用了自动装箱,而自动装箱之前的自动拆箱过程仍是有异常的
- 因为该表达式的返回值类型为int,是基本类型,而null不是基本类型,所以需要自动拆箱将值赋给返回值,而“
- 如
4. 代码
public class Test {
private static void test(boolean flag1, boolean flag2) {
Double d = flag1 ? new Double(1d) : flag2 ? new Double(2d) : new Double(3d);
Float f = flag1 ? new Float(1f) : flag2 ? new Float(2f) : new Float(3f);
Long l = flag1 ? new Long(1L) : flag2 ? new Long(2L) : new Long(3L);
Integer i = flag1 ? new Integer(1) : flag2 ? new Integer(2) : new Integer(3);
Short s = flag1 ? new Short((short) 1) : flag2 ? new Short((short) 2) : new Short((short) 3);
Character c = flag1 ? new Character('1') : flag2 ? new Character('2') : new Character('3');
Byte by = flag1 ? new Byte((byte) 1) : flag2 ? new Byte((byte) 2) : new Byte((byte) 3);
Boolean bo = flag1 ? new Boolean(true) : flag2 ? new Boolean(true) : new Boolean(false);
double d1 = flag1 ? new Integer(1) : flag2 ? new Integer(2) : new Double(3.0);
float f1 = flag1 ? new Integer(1) : flag2 ? new Long(2) : new Float(3.0);
long l1 = flag1 ? new Integer(1) : flag2 ? new Integer(2) : new Long(3);
int i1 = flag1 ? new Integer("1") : flag2 ? new Byte("2") : new Short("3");
short s1 = flag1 ? new Byte("1") : flag2 ? new Byte("3") : new Short("4");
char c1 = flag1 ? '1' : flag2 ? '2' : new Character('3');
byte by1 = flag1 ? new Byte("1") : flag2 ? new Byte("2") : (byte) 3;
boolean bo1 = flag1 ? false : flag2 ? false : new Boolean(true);
int i2 = flag1 ? new Short("1") : flag2 ? new Byte("2") : new Character('3');
Serializable serializable1 = flag1 ? 1 : flag2 ? 3.3 : "hello";
Serializable serializable2 = flag1 ? 1 : flag2 ? 3.3 : false;
Serializable serializable3 = flag1 ? 1 : flag2 ? 3.3 : Boolean.TRUE;
Object o = flag1 ? 1 : flag2 ? new Object() : "hello";
}
}
5. 字节码
// java文件
public class Test {
public static void main(String[] args) {
// 基本类型接返回值
int res = false ? 1 : false ? 2 : null;
}
}
// 字节码文件
0 aconst_null
1 checkcast #2 <java/lang/Integer>
4 invokevirtual #3 <java/lang/Integer.intValue : ()I> // 自动拆箱
7 istore_1
8 return
// java文件
public class Test {
public static void main(String[] args) {
// 包装类型接返回值
Integer res = false ? 1 : false ? 2 : null;
}
}
// 字节码文件
0 aconst_null
1 checkcast #2 <java/lang/Integer>
4 invokevirtual #3 <java/lang/Integer.intValue : ()I> // 自动拆箱
7 invokestatic #4 <java/lang/Integer.valueOf : (I)Ljava/lang/Integer;> // 自动装箱
10 astore_1
11 return
从上面字节码可以看出,用包装类型去接返回值,是在自动拆箱后多了一步自动装箱