Casting a generic class. (cast) vs Class.cast()
I think that both methods do the same.
No, they don't. Because at execution time, the first code doesn't know the type of T
due to type erasure. That means the cast basically does nothing within the method. The calling code might implicitly cast to whatever T
it is specifying, but if the caller is another generic method (with the T
used here as another type parameter), even that wouldn't want.
Here's a simple example of that:
public class Test {
public static void main(String[] args) {
Object o = new Object();
// No exception
Test.<String>fakeCast(o);
// Exception at the point of assignment:
// the code is effectively String x = (String) ...;
String x = Test.<String>fakeCast(o);
}
private static <T> T fakeCast(Object o) {
return (T) o;
}
}
The second code knows the type of T
in the form of Class<T>
, so it can perform a real cast at execution time, at exactly the point you're performing it.
Well explained answer by Jon Skeet. I'd like to add an example here so that the differences can be observed clearly
Class.cast()
public class Test{
public static void main(String[] args){
Object o = new Object();
Test.castMethod(o, String.class); //Exception is thrown here
}
public static <T> T castMethod (Object o, Class<T> tClass){
return tClass.cast(o)
}
}
Output:
Exception in thread "main" java.lang.ClassCastException: Cannot cast java.lang.Object to java.lang.String
at java.base/java.lang.Class.cast
at com.test.Test.castMethod
Down-casting Object.class
object to String.class
is illegal here as they are not compatible.
By using Class.cast()
, the casting does take place in castMethod()
and hence throws ClassCastException
. That's what the Real Casting means as stated by Jon.
Cast Operator
public class Test{
public static void main(String[] args){
Object o = new Object();
Test.<String>castMethod(o); //No Exception
String x = Test.<String>castMethod(o); //Exception is thrown here
}
public static <T> T castMethod(Object o){
return (T) o;
}
}
Output:
Exception in thread "main" java.lang.ClassCastException: java.base/java.lang.Object cannot be cast to java.base/java.lang.String
at com.test.Test.main
From the output, you can see that the ClassCastException
is thrown at main()
, unlike Class.cast() which throws exception at castMethod()
. That's why Jon named it as Fake Casting, as the casting is actually done when the result of castMethod
is assigned to the String variable. If the castMethod
is called with the result being ignored, no exception will be seen at all.
Also, return (T) o
will give you an ugly Unchecked Cast
warning from the linter