10

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?

2
  • I'd assume because .class is evaluated at compile time, but .getClass() is evaluated at runtime. Commented Oct 8, 2014 at 17:40
  • 6
    The compiler should have warned you about the use of the raw type Class instead of the parameterized type Class<>, which would be your hint that something is wrong with that declaration. Commented Oct 8, 2014 at 17:53

2 Answers 2

16

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.

Sign up to request clarification or add additional context in comments.

Comments

11

The Java Language Specification states

The type of C.class, where C is the name of a class, interface, or array type (§4.3), is Class<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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.