三元运算符的奇怪行为

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)));

它不回答 为什么 发生,但回答 如何 发生。至少我希望如此。