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