编译器放弃我的类型转换?
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
。所以我在 bar
、hasLongId()
中添加了一个方法并尝试了这个:(此时你可能认为这不是一个好的设计,但这不是我现在的问题)
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);
这个有效。所以显然它与三元运算符有关。但为什么?有人可以解释这里发生了什么吗?
这是一个很好的问题,深入探讨了三元表达式语义的细节。不,您的编译器没有损坏或对您耍花招。
在这种情况下,如果三元表达式的第二个和第三个操作数的类型是long
和int
,那么结果类型总是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
这本质上意味着什么,当你有一个三元表达式时,表达式的结果类型必须是静态可确定的(在编译时)。第二个和第三个操作数必须强制转换为单一类型,即表达式的类型。如果两个操作数都是数字类型,则会启动二进制数字提升以确定表达式的最终类型。
我对我必须做些什么才能使这段代码起作用感到困惑。好像编译器优化掉了我需要的类型转换,或者这里还有其他我不明白的地方。
我有各种对象存储在实现接口 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
。所以我在 bar
、hasLongId()
中添加了一个方法并尝试了这个:(此时你可能认为这不是一个好的设计,但这不是我现在的问题)
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);
这个有效。所以显然它与三元运算符有关。但为什么?有人可以解释这里发生了什么吗?
这是一个很好的问题,深入探讨了三元表达式语义的细节。不,您的编译器没有损坏或对您耍花招。
在这种情况下,如果三元表达式的第二个和第三个操作数的类型是long
和int
,那么结果类型总是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
这本质上意味着什么,当你有一个三元表达式时,表达式的结果类型必须是静态可确定的(在编译时)。第二个和第三个操作数必须强制转换为单一类型,即表达式的类型。如果两个操作数都是数字类型,则会启动二进制数字提升以确定表达式的最终类型。