三元运算符的奇怪行为
Strange behavior of ternary operator
我观察到奇怪的行为,在干净的 "test" 应用程序中我有这个简单的控制器:
(Grails 2.5.0,Java Oracle 8u45,GNU/Linux Debian 7)
package test
class DiController {
def ok() {
double d = 0d
int i = (int)d
def r = []
r << i
if (true) {
r << i
}
else {
r << d
}
if (false) {
r << i
}
else {
r << d
}
render r.toString()
}
def bug() {
double d = 0d
int i = (int)d
def r = []
r << i
r << (true ? i : d) // why Integer will be serialized like Double ?
r << (false ? i : d)
render r.toString()
}
}
/test/di/ok 呈现 [0, 0, 0.0]
,但 /test/di/bug 呈现 [0, 0.0, 0.0]
.
似乎三元运算符转换了结果值,但我在Groovy控制台中没有观察到这种转换。这是 Grails 特有的吗?
(Graeme Rocher 在此处将此标记为 "not a bug":https://github.com/grails/grails-core/issues/8994,因此应该是已知行为)
编辑: 我使用较旧的 Groovy (1.8.6) 进行测试 "out of Grails",Groovy 2.4.3 描述了行为(给出 [0, 0.0, 0.0]
),所以它实际上与 Grails 无关。
我认为那是因为 Groovy 试图为不兼容的类型找到一个共同点。这是各种三元运算符使用的示例:
println ((true ? (0 as Integer) : (0.0 as Float)).class)
println ((true ? (0 as Float) : (0.0 as Double)).class)
println ((true ? (0 as Integer) : (0 as Long)).class)
println ((true ? (0 as Integer) : (0 as Byte)).class)
这就是 Groovy 将其编译成 Java
的方式
var1[1].callCurrent(this, var1[2].callGetProperty(true?(Float)ScriptBytecodeAdapter.castToType((Integer)ScriptBytecodeAdapter.asType(Integer.valueOf(0), Integer.class), Float.class):(Float)ScriptBytecodeAdapter.asType($const[=11=], Float.class)));
var1[3].callCurrent(this, var1[4].callGetProperty(true?(Double)ScriptBytecodeAdapter.castToType((Float)ScriptBytecodeAdapter.asType(Integer.valueOf(0), Float.class), Double.class):(Double)ScriptBytecodeAdapter.asType($const[=11=], Double.class)));
var1[5].callCurrent(this, var1[6].callGetProperty(true?(Long)ScriptBytecodeAdapter.castToType((Integer)ScriptBytecodeAdapter.asType(Integer.valueOf(0), Integer.class), Long.class):(Long)ScriptBytecodeAdapter.asType(Integer.valueOf(0), Long.class)));
var1[7].callCurrent(this, var1[8].callGetProperty(true?(Integer)ScriptBytecodeAdapter.asType(Integer.valueOf(0), Integer.class):(Integer)ScriptBytecodeAdapter.castToType((Byte)ScriptBytecodeAdapter.asType(Integer.valueOf(0), Byte.class), Integer.class)));
在所有情况下,它都会进行大量类型转换,只是为了让 true/false 结果分支一致并使它们 return 成为同一类型。令人惊讶的是,这仅适用于数值。其他值区别对待。
println ((true ? ("s") : (0 as Byte)).class)
println ((false ? ("s") : (0 as Byte)).class)
println ((true ? (new Date()) : (0 as Byte)).class)
被翻译成Java喜欢
var1[9].callCurrent(this, var1[10].callGetProperty(true?"s":(Byte)ScriptBytecodeAdapter.asType(Integer.valueOf(0), Byte.class)));
var1[11].callCurrent(this, var1[12].callGetProperty(false?"s":(Byte)ScriptBytecodeAdapter.asType(Integer.valueOf(0), Byte.class)));
var1[13].callCurrent(this, var1[14].callGetProperty(true?var1[15].callConstructor(Date.class):(Byte)ScriptBytecodeAdapter.asType(Integer.valueOf(0), Byte.class)));
它不回答 为什么 发生,但回答 如何 发生。至少我希望如此。
我观察到奇怪的行为,在干净的 "test" 应用程序中我有这个简单的控制器:
(Grails 2.5.0,Java Oracle 8u45,GNU/Linux Debian 7)
package test
class DiController {
def ok() {
double d = 0d
int i = (int)d
def r = []
r << i
if (true) {
r << i
}
else {
r << d
}
if (false) {
r << i
}
else {
r << d
}
render r.toString()
}
def bug() {
double d = 0d
int i = (int)d
def r = []
r << i
r << (true ? i : d) // why Integer will be serialized like Double ?
r << (false ? i : d)
render r.toString()
}
}
/test/di/ok 呈现 [0, 0, 0.0]
,但 /test/di/bug 呈现 [0, 0.0, 0.0]
.
似乎三元运算符转换了结果值,但我在Groovy控制台中没有观察到这种转换。这是 Grails 特有的吗?
(Graeme Rocher 在此处将此标记为 "not a bug":https://github.com/grails/grails-core/issues/8994,因此应该是已知行为)
编辑: 我使用较旧的 Groovy (1.8.6) 进行测试 "out of Grails",Groovy 2.4.3 描述了行为(给出 [0, 0.0, 0.0]
),所以它实际上与 Grails 无关。
我认为那是因为 Groovy 试图为不兼容的类型找到一个共同点。这是各种三元运算符使用的示例:
println ((true ? (0 as Integer) : (0.0 as Float)).class)
println ((true ? (0 as Float) : (0.0 as Double)).class)
println ((true ? (0 as Integer) : (0 as Long)).class)
println ((true ? (0 as Integer) : (0 as Byte)).class)
这就是 Groovy 将其编译成 Java
的方式var1[1].callCurrent(this, var1[2].callGetProperty(true?(Float)ScriptBytecodeAdapter.castToType((Integer)ScriptBytecodeAdapter.asType(Integer.valueOf(0), Integer.class), Float.class):(Float)ScriptBytecodeAdapter.asType($const[=11=], Float.class)));
var1[3].callCurrent(this, var1[4].callGetProperty(true?(Double)ScriptBytecodeAdapter.castToType((Float)ScriptBytecodeAdapter.asType(Integer.valueOf(0), Float.class), Double.class):(Double)ScriptBytecodeAdapter.asType($const[=11=], Double.class)));
var1[5].callCurrent(this, var1[6].callGetProperty(true?(Long)ScriptBytecodeAdapter.castToType((Integer)ScriptBytecodeAdapter.asType(Integer.valueOf(0), Integer.class), Long.class):(Long)ScriptBytecodeAdapter.asType(Integer.valueOf(0), Long.class)));
var1[7].callCurrent(this, var1[8].callGetProperty(true?(Integer)ScriptBytecodeAdapter.asType(Integer.valueOf(0), Integer.class):(Integer)ScriptBytecodeAdapter.castToType((Byte)ScriptBytecodeAdapter.asType(Integer.valueOf(0), Byte.class), Integer.class)));
在所有情况下,它都会进行大量类型转换,只是为了让 true/false 结果分支一致并使它们 return 成为同一类型。令人惊讶的是,这仅适用于数值。其他值区别对待。
println ((true ? ("s") : (0 as Byte)).class)
println ((false ? ("s") : (0 as Byte)).class)
println ((true ? (new Date()) : (0 as Byte)).class)
被翻译成Java喜欢
var1[9].callCurrent(this, var1[10].callGetProperty(true?"s":(Byte)ScriptBytecodeAdapter.asType(Integer.valueOf(0), Byte.class)));
var1[11].callCurrent(this, var1[12].callGetProperty(false?"s":(Byte)ScriptBytecodeAdapter.asType(Integer.valueOf(0), Byte.class)));
var1[13].callCurrent(this, var1[14].callGetProperty(true?var1[15].callConstructor(Date.class):(Byte)ScriptBytecodeAdapter.asType(Integer.valueOf(0), Byte.class)));
它不回答 为什么 发生,但回答 如何 发生。至少我希望如此。