I've got the following 3 tests. The first two work, the last one doesn't. My motivation for asking this question is that I'd like to be able to cast object A so that it has the same class as object B, when A is known to be a subtype of B.
@Test
public void testWorks() {
Object bar = "foobar";
String blah = (String) bar;
System.out.println(blah); // Outputs foobar
}
@Test
public void testAlsoWorks() {
Object bar = "helloworld";
String blah = String.class.cast(bar);
System.out.println(blah); // Outputs helloworld
}
@Test
public void testfails() {
Object bar = "foobar";
String thetype = "hello";
Class stringclass = thetype.getClass();
String blah = stringclass.cast(bar); // Compiler error: incompatible types: Object cannot be converted to String
System.out.println(blah);
}
Can anyone explain why the last case fails when the first two succeed, and why this is the case? And what would be a better approach to achieve this?
You need to specify the type parameter of Class:
Class<String> stringclass = (Class<String>) thetype.getClass();
or
Class<? extends String> stringclass = thetype.getClass();
java.lang.Class.cast(Object obj) casts an object to the class or interface represented by this Class object.
Without specifying the type, you are not telling the compiler what class or interface is represented by the Class instance stringclass.
When you call String.class, the Class object is implicitly parametrized with the String type.
The Java Language Specification states
The type of
C.class, whereCis the name of a class, interface, or array type (§4.3), isClass<C>.
So the type of the expression
String.class
is Class<String>. Class is a generic class where the method cast uses the generic type variable in its return type. So the result of
String.class.cast(bar);
is an expression of type T, where T has been bound to String in this invocation.
The return type of Object#getClass() is Class<? extends T> where T is the erased type of the expression it's invoked on.
In this case, that is
Class<? extends String>
However, you are assigning it to a raw reference.
Class stringclass = thetype.getClass();
Since the variable stringclass is raw, any uses of its methods that depend on the generic type variable are erased.
So Class#cast(Object) now has a return type of Object which is not assignable to a String variable.
Had you done
Class<? extends String> stringclass = thetype.getClass();
you'd be fine.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With