Integer.longValue() 如果没有先分配给 Number 会抛出异常

Integer.longValue() does throw an Exception if not assigned to Number first

public Long getId()
{
    Some code ...

    TypedQuery<Long> query = entityManager.createQuery(sQuery, Long.class);
    return query.getSingleResult();
}

从这段代码中,我得到了从 IntegerLongClassCastException。我在调试器中检查了 query.getSingleResult(); 并添加了 Integer 5.

如果我将代码更改为 query.getSingleResult().longValue();,它仍然不起作用。我得到同样的例外。但是如果我使用

Number tmp = query.getSingleResult();
return tmp.longValue();

有效。我的问题是 为什么第一个解决方案不起作用? Surley 我可以更改我的查询,但我只想知道为什么第二个解决方案有效而第一个无效。

请随意更改我的问题的标题。提前致谢!

您的查询实际上 returns 一个 Integer 但您通过调用 entityManager.createQuery(sQuery, Long.class).

假装它 returns 一个 Long

现在当你执行

query.getSingleResult()
query.getSingleResult().longValue()

由于您的泛型声明,编译器插入了对 Long 的转换。因此这实际上被执行了:

(Long)query.getSingleResult()
((Long)query.getSingleResult()).longValue()

并抛出 ClassCastException 因为 Integer 不是 Long.

当你打电话时

Number tmp = query.getSingleResult();

它实际执行

Number tmp = (Number)query.getSingleResult();

并且此代码成功,因为 IntegerNumber

所以不是 longValue() 抛出异常,而是之前发生的转换。

createQuery 的规范说:

The select list of the query must contain only a single item, which must be assignable to the type specified by the resultClass argument.

因此您遇到了问题,因为 Integer 无法分配给 Long。然而,有趣的是考虑为什么一个版本抛出异常而另一个版本不抛出异常。

我将使用更熟悉的类来解释使用Number类型的临时变量和不使用类型的临时变量之间的区别。

这个例子抛出一个 ClassCastException:

 List<Long> list = (List<Long>) (List) Collections.singletonList(5); // Very dodgy.
 list.get(0).longValue();

原因是泛型只是一个编译时特性,必要时会插入不可见的强制转换。实际上,list.get(0) 只是一个 Object,而 Object 没有方法 longValue。转换的类型 不是 使用该方法的最不明确的类型,而只是 Long。因此第二行变成

((Long) list.get(0)).longValue();

并抛出 ClassCastException

另一方面,如果您这样做

Number a = list.get(0);
a.longValue();

不需要不可见的转换来键入 Long,因为 Number 已经有一个方法 longValue。此版本不抛出异常。

第一种方法不可行,因为你使用了错误的方法来进行此转换。这个Class的树是这样的:

正确的 Cast 方法是先将 Integer 转换为 Number,然后调用 longValue() 方法转换为 Long。

第二种方法有效,因为您按照正确的步骤投射了这两种类型的数字。

看看下面的打印。编译器不喜欢这种强制转换方式。不仅适用于 Long,也适用于 BigDecimal 以及其他数字