2
public Long getId()
{
    Some code ...

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

From this code i get a ClassCastException from Integer to Long. I checked query.getSingleResult(); in the debugger and i conatined the Integer 5.

If i change the code to query.getSingleResult().longValue(); it still does not work. I get the same Exception. But if i use

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

it works. My Question is Why doesnt the first solution work? Surley i can change my query, but i only want to know why the secound works and the first do not.

Feel free to change the title of my question. Thanks in advance!

kai
  • 6,702
  • 22
  • 38

3 Answers3

7

Your query actually returns a Integer but you pretend that it returns a Long by calling entityManager.createQuery(sQuery, Long.class).

Now when you execute

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

the compiler inserts a cast to Long because of your generics declaration. Therefore this is actually executed:

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

and a ClassCastException is thrown because Integer is not a Long.

When you call

Number tmp = query.getSingleResult();

it actually performs

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

and this code succeeds since Integer is a Number.

So it is not that longValue() throws an Exception but the cast that happens before.

wero
  • 32,544
  • 3
  • 59
  • 84
  • Your answer is not explaining why `query.getSingleResult().longValue();` statement not working. This should work, because Number, Integer, Long all have `longValue` method. – rajuGT Nov 04 '15 at 11:54
  • 2
    @rajuGT `query.getSingleResult().longValue()` also compiles to `((Long)query.getSingleResult()).longValue()` – wero Nov 04 '15 at 11:59
  • if it did not, there would not be any `longValue()` method available (Object does not have it). So the compiler needs to insert the cast to the generic type (which you have to correctly specify here, and if you get it wrong, you end up with a class cast error). – Thilo Nov 04 '15 at 12:04
  • `longValue` casts the `Integer` to `long` – Murat Karagöz Nov 04 '15 at 12:08
  • Thanky you. That perfectly solves my confusion. I thought the cast to `Long` does also happen before the cast to `Number`. – kai Nov 04 '15 at 12:26
  • Do you know why the cast from the generic declaration is inserted where i call the `getSingleResult();` method? I think it makes more sense when the cast is inserted in the return statement of the `getSingleResult();` method, because the method is declated to return a Long. – kai Nov 04 '15 at 12:57
  • 1
    @Kai That is how Java implements generics. `TypedQuery.getSingleResult()` promises to return a `T` but when compiled it returns `Object` due to type erasure. Therefore the cast is inserted on the call site. – wero Nov 04 '15 at 13:04
0

The specification for createQuery says:

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

You are therefore getting problems because Integer is not assignable to Long. However it is interesting to consider why one version throws an exception whereas the other does not.

I will use more familiar classes to explain the difference between using a temp variable of type Number and not.

This example throws a ClassCastException:

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

The reason is that generics are just a compile-time feature where invisible casts are inserted if necessary. Really, list.get(0) is just an Object, and Object does not have a method longValue. The type of the cast is not the least-specific type with that method, but simply Long. Therefore the second line becomes

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

and a ClassCastException is thrown.

If, on the other hand, you do

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

there is no need for an invisible cast to type Long because Number already has a method longValue. This version does not throw an exception.

Paul Boddington
  • 37,127
  • 10
  • 65
  • 116
0

The first way don't work because you used the wrong way to make this cast. The tree of this Class is like this:

enter image description here

The right way to do this Cast is first cast Integer to Number and then transform to Long calling longValue() method.

The second way works because you follow the correct steps to cast this two type of numbers.

Take a look to the follow print. The compiler dont like this way of casts. Not just for Long, for BigDecimal too as well as other numbers

enter image description here