Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does casting based on Class.class work but not getClass?

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?

like image 803
user3170530 Avatar asked Oct 08 '14 17:10

user3170530


2 Answers

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.

like image 195
M A Avatar answered Nov 10 '22 21:11

M A


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.

like image 45
Sotirios Delimanolis Avatar answered Nov 10 '22 22:11

Sotirios Delimanolis