Are generic methods really able to distinguish on the return type?
Are generic methods really able to distinguish on the return type?
In some legacy code I found the following method:
public static <T> T cast(Object o) return (T) o;
This is used to cast arbitrary objects to any type only based on the return type like this:
List<SomeType> foo = cast(someObject);
How is that cast method able to cast to the correct type?
2 Answers
2
The method does not do anything. The actual casting happens on the assignment.
Here is how you can see what is going on:
public static <T> T cast(Object o)
try
return (T) o;
catch (ClassCastException cce)
// This never gets printed
System.out.println("Cast has failed");
throw cce;
finally
System.out.println("Cast succeeded");
Now invoke the method with arguments that would cause the cast to throw:
List<SomeType> foo = cast(new Integer(5));
You will see the "Cast succeeded" printout, and then ClassCastException
would be thrown inside the method doing the call (demo).
ClassCastException
The reason for this behavior is that Java implements generics using type erasure. If you would like to implement casting in a way that fails inside the method, pass Class<T>
as an additional argument:
Class<T>
public static <T> T cast(Object o, Class<T> cls)
return cls.cast(o);
@michas Well, Java's generics are implemented the "gentleman's agreement" way: there are situations when the compiler has to trust the programmer too much.
– dasblinkenlight
Sep 11 '18 at 15:12
It isn't.
This is an unchecked cast, and will fail at runtime if the types you're trying to cast to don't align.
As a simple case, observe what happens when we attempt to use this method to cast a list of Integer
s to a list of BigInteger
s.
Integer
BigInteger
List<Integer> intList = Lists.newArrayList(1, 2, 3, 4, 5);
List<BigInteger> bigIntList = cast(intList);
for(BigInteger i : bigIntList)
System.out.println(i.doubleValue());
Or, for a less contrived but still broken cast, trying to cast simple types across.
Integer i = 10;
BigInteger bigI = cast(i);
System.out.println(bigI.doubleValue());
This legacy code is dangerously broken and should not be relied on for any kind of casting. In fact, the kind of casting that is desired from this should be handled by a transform method instead.
I should note - casting is you telling the Java compiler that you not only know what you're doing, but you're also accepting the risk if it fails, which is one reason why the compiler won't warn you about the invalid types. If this were done properly with generics (and I'm not entirely convinced it's possible with generics), then you would get a compile-time warning indicating what it is you were doing wrong.
It is really part of a production system. (Which of course does not make things better...) The author was sure that the object contains a suitable type, but the compiler complained. - Looks like that was his way to please the compiler. - I was only surprised that this really made the types match. - After all it looks like
T
would be inferred by the return type only. - I was sure that is not possible.– michas
Sep 11 '18 at 15:15
T
Yeah - I added a blurb about that since I glanced at your comments in the other answer. Casts let you take over and you now have to be responsible for those casts since the compiler isn't.
– Makoto
Sep 11 '18 at 15:16
Thanks for contributing an answer to Stack Overflow!
But avoid …
To learn more, see our tips on writing great answers.
Required, but never shown
Required, but never shown
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
Looks like that method was a way to stop the compiler about complaining about the wrong type. I never said it is a good way do do such a thing, I was only surprised that it really works without complaining. (At least with Java7.)
– michas
Sep 11 '18 at 15:09