编译器放弃我的类型转换?

Compiler dropping my type conversion?

我对我必须做些什么才能使这段代码起作用感到困惑。好像编译器优化掉了我需要的类型转换,或者这里还有其他我不明白的地方。

我有各种对象存储在实现接口 Foo 的数据库中。我有一个对象 bar,它包含我用来检索 Foo 对象的数据。 bar 有这些方法:

Class getFooClass()

Long getFooId()

我将 class 和 ID 传递给具有此签名的方法,该方法委托休眠根据其 class 和 ID 检索主题:

public <T> T get(Class<T> clazz, Serializable id);

Foo 有不同的实现者,其中一些休眠对象具有 Long id,而另一些具有 Integer id。尽管此方法接受任何一个,但在更远的地方它最好有正确的那个。因此,当我尝试在具有 Integer id 的对象上调用 get() 时,如下所示,我可以理解地收到一个错误,抱怨我提供了一个 Long,而 Integer 是要求:

    get(bar.getFooClass(), bar.getFooId());

这里没有休眠问题,我只需要提供一个需要 Integer id 的 Integer 和需要 Long id 的 Long。所以我在 barhasLongId() 中添加了一个方法并尝试了这个:(此时你可能认为这不是一个好的设计,但这不是我现在的问题)

get(bar.getFooClass(),
    bar.hasLongId() ? bar.getFooId() : bar.getFooId().intValue());

它仍然抱怨我提供了 Long。这似乎很奇怪。然后我试了这个:

get(bar.getFooClass(),
    bar.hasLongId() ? bar.getFooId() 
                    : new Integer(bar.getFooId().intValue()));

同样的错误!怎么会这样?所以我在调试器中单步执行,是的,它单步执行了 intValue() 也通过了 Integer 构造函数,但是在 get 方法中,传递的参数实际上是一个 Long——从 getFooId().

返回的相同 Long 对象

我不明白发生了什么,所以我只是尝试各种事情:

Integer intId = bar.getFooId().intValue();
get(bar.getFooClass(), bar.hasLongId() ? bar.getFooId() : intId);   
// same error

Serializable id = bar.hasLongId() ? bar.getFooId() 
                                : new Integer(bar.getFooId().intValue());
get(bar.getFooClass(), id); 
// same error

最后:

Serializable id;
if (bar.hasLongId()) {
    id = bar.getFooId();
} else {
    id = bar.getFooId().intValue();
}
get(bar.getFooClass(), id);

这个有效。所以显然它与三元运算符有关。但为什么?有人可以解释这里发生了什么吗?

这是一个很好的问题,深入探讨了三元表达式语义的细节。不,您的编译器没有损坏或对您耍花招。

在这种情况下,如果三元表达式的第二个和第三个操作数的类型是longint,那么结果类型总是long。这是由于 binary numeric promotion.

根据 JLS (Java Language Specification):

..., binary numeric promotion is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands.

由于 二进制数字提升 的规则 #1,这些值正在拆箱:

If any operand is of a reference type, it is subjected to unboxing conversion

这本质上意味着什么,当你有一个三元表达式时,表达式的结果类型必须是静态可确定的(在编译时)。第二个和第三个操作数必须强制转换为单一类型,即表达式的类型。如果两个操作数都是数字类型,则会启动二进制数字提升以确定表达式的最终类型。