【问题标题】:Compiler dropping my type conversion?编译器放弃我的类型转换?
【发布时间】:2016-09-21 00:12:26
【问题描述】:

我不知道我必须做什么才能让这段代码正常工作。似乎编译器优化了我需要的类型转换,或者这里还有其他我不明白的东西。

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

Class getFooClass()

Long getFooId()

我将类和 ID 传递给具有此签名的方法,该方法委托给 hibernate,后者根据其类和 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 需要Integer id 和Long 需要Long id。于是我在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——同一个Long 对象从getFooId()返回。

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

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

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

【问题讨论】:

  • 信息不足。不要解释代码,也不要总结错误信息。根据您给我们的信息,我可能会或可能无法找出问题所在,但我不会花太多时间在这上面,因为我可以告诉您,您还没有给我们任何完整的东西。
  • 与你的问题无关,但是你为什么不让getFooId()返回一个Number而不是额外的方法和类型转换?
  • 投票重新打开,因为另一个问题没有讨论三元表达式或装箱值。
  • 同意@shmosel,最初标记为重复的问题不同。所以我重新打开了这个问题

标签: java static-typing


【解决方案1】:

这是一个很好的问题,涉及到三元表达式语义的细节。不,您的编译器没有损坏或欺骗您。

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

根据JLS (Java Language Specification)

...,二进制数值提升应用于操作数类型,条件表达式的类型是第二个和第三个操作数的提升类型。

由于Binary Numeric Promotion 的规则 #1,这些值正在被取消装箱:

如果任何操作数是引用类型,则对其进行拆箱转换

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

【讨论】:

  • 明确装箱的类型呢?是不是开箱了?为什么?
  • 好答案。正如@shmosel 所提到的,它会更完整地说明为什么编译器在遇到带有condition ? Long : int condition? Long : Integer 的三元运算符时,决定将Long 自动拆箱以形成condition ? long : intcondition? Long : Integer 的形式在这里也应该有效?
  • 编辑回答上述问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-16
  • 1970-01-01
  • 1970-01-01
  • 2011-10-10
  • 2023-03-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多