Reputation: 1191
In this code, why can type not be declared as Class<? extends B>
?
public class Foo<B> {
public void doSomething(B argument) {
Class<? extends Object> type = argument.getClass();
}
}
Upvotes: 10
Views: 202
Reputation: 533530
This problem is that Java's syntax doesn't allow getClass() to say it's returning a type which matches the class its defined in, and this is not a special case as far as the compiler is concerned. So you are forced to cast the result.
There are many cases where you would like to be able to specify this
type, e.g. for chaining, so I would hope they include this feature one day.
You could write
Class<? extends this> getClass();
or
public this clone(); // must return a type of this class.
or
class ByteBuffer {
this order(ByteOrder order);
}
class MappedByteBuffer extends ByteBuffer {
}
// currently this won't work as ByteBuffer defines order()
MappedByteBuffer mbb = fc.map(MapMode.READ_WRITE, 0, fc.size())
.order(ByteOrder.nativeOrder());
Upvotes: 4
Reputation: 40713
Because the class of a given object is not guaranteed to be the same as the type it is stored in.
eg.
Object o = "some string";
Class<Object> clazz = o.getClass(); // actually Class<String>
By looking at the type you should expect the Class for Object
, but in reality you get the class for String
. What problem is this you might ask -- Object
is a superclass of String
, so String
implements all the methods implemented by Object
.
The problems are that when getting a Field
the class will return the fields of the actual class and not the generic type. In addition, whilst Method
is able to invoke the correct method if there is an overriding method in the given object, it is not able to do the reverse and find an implementation of the method that will work on the given object.
For instance, Object declares hashCode
, so all objects have a hash code method. However, the following will produce a runtime exception:
Object.class.getMethod("hashCode").invoke("some string"); // works
String.class.getMethod("hashCode").invoke(new Object()); // fails
This is because the Method
object for hashCode
is expecting a String
. It's expecting to generate a hash code from the sequence of characters, but the provided object does not have a char array for the method to work on, so it fails.
Meaning the following looks like it should work, but won't because the actual method returned by getMethod
is the hash code method for String
.
Object obj = "string";
Class<Object> clazz = obj.getClass();
clazz.getMethod("hashCode").invoke("another string");
Upvotes: 0
Reputation: 1575
Object.getClass()
is defined to return a Class, where T is the statically known type of the receiver (the object getClass() is called on). Take special note of the vertical bars, the erasure operator. The erasure of a type variable is the erasure of its leftmost bound. In your case that's the implicit bound Object. So you get back a Class, not a Class<? extends T>
.
The right way to do it is,
abstract class AbstractExecutor<E> {
public void execute() throws Exception {
List<E> list = new ArrayList<E>();
Class<E> cls = (Class<E>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
E e = cls.getConstructor(String.class).newInstance("Gate");
list.add(e);
System.out.println(format(list));
}
// ...
}
Upvotes: 0
Reputation: 3710
This is all about generic type erasure
. From here:
Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods. [at compile time]
So you cannot get Class
of actual type of B
but only ?
or ? extends Object
.
If your bounds will be turn into <B extends SomeClass>
instead of <B>
only then you can fetch Class
object of type <? extends SomeClass>
.
Upvotes: 0