Casting a generic class. (cast) vs Class.cast()

Viewed 9100

I have searched for my use case and found some interesting answers but they are not as suitable as i need. What would the appropriate way to do this:

@SuppressWarnings("unchecked")
public <T> T newInstance(String name) throws ClassCastException, InstantiationException, IllegalAccessException, ClassNotFoundException {
    return (T) loadClass(name).newInstance();
}

or a little different:

public <T> T newInstance(String name, Class<T> c) throws ClassCastException, InstantiationException, IllegalAccessException, ClassNotFoundException {
    return c.cast(loadClass(name).newInstance());
}

I think that both methods do the same. From my point of view method 1 is better because of less parameters. Boths throw a ClassCastException which will be fine for me. Truly, the @SuppressWarnings("unchecked") annotation is not nice.

Can someone tell me if there are any advantages for one method towards the other?

Edit: Jon Skeet's answer is correct. The following snippet can provide additional clarification.

public class Test {
    public static void main(String[] args) {
        Object o = new Object();
        // Exception at c.cast(o)
        Test.<String>realCast(o, String.class);
    }

    private static <T> T realCast(Object o, Class<T> c) {
        return c.cast(o);
    }
}

Using realCast() produces an Exception when o can't be cast to c. In comparison to that fakeCast() only gives a promise that the method's result is type of T.

2 Answers

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

Related