If I have the following interface:
interface MyElementProcessor<T> {
void doSomethingWith(T element);
Class<T> getElementClass();
/* more functions... */
}
Say that I want to implement this for a generic type like Collection<T>. How do I implement getElementClass()? My best attempt so far is something like this:
class MyCollectionProcessor<T> implements MyElementProcessor<Collection<T>> {
public void doSomethingWith(Collection<T> element) { /* ... */ }
public Class<Collection<T>> getElementClass() {
// Ugly, but the only way I have found to cast to Class<Collection<T>>:
return (Class<Collection<T>>) (Object) Collection.class;
}
}
I know that there is no way to specify a Class<Collection<T>> literal in Java, but why isn't it possible to cast Collection.class directly to Class<Collection<T>>? Is there a better way than casting via Object?
why isn't it possible to cast Collection.class directly to
Class<Collection<T>>?
Collection.class has type Class<Collection>. Java will not compile this type cast because it can be proved that it cannot succeed. There is probably no type that is a subtype of both Class<Collection> and Class<Collection<T>>.
In Generics, Foo<A> is never a subtype of Foo<B> if A and B are different and they are types (not wildcards), regardless of the relationship between A and B. Therefore, the only subtypes of Class<Collection> are SubclassOfClass<Collection>. And similarly the only subtypes of Class<Collection<T>> are SubclassOfClass<Collection<T>>. Since these do not intersect, there is no way this cast can theoretically succeed.
Is there a better way than casting via Object?
You could cast via Class<?>; but it is not much better:
return (Class<Collection<T>>) (Class<?>) Collection.class;
You could pass the class literal to the constructor:
private final Class<Collection<T>> clazz;
public MyCollectionProcessor(Class<Collection<T>> clazz) {
this.clazz = clazz;
}
public Class<Collection<T>> getElementClass() {
return clazz;
}
No cast needed but an extra parameter...
Because of Java generics handling, a Class<List<Integer>> class cannot be obtained (without unchecked cast), but will be Class<List> or Class<List<?>>.
Therefore, if you're working with parameterized type in your ElementProcessor, i suggest that you change your method to be Class<? super T> getElementClass()
Two examples :
interface MyElementProcessor<T> {
void doSomethingWith(T element);
Class<? super T> getElementClass();
/* more functions... */
}
SimpleElementProcessor implements MyElementProcessor<Integer> {
public void doSomethingWith(Collection<Integer> element) { /* ... */ }
public Class<Integer> getElementsClass() {
return Integer.class;
}
}
CollectionElementProcessor<E> implements MyElementProcessor<Collection<E>> {
public void doSomethingWith(Collection<Collection<E>> element) { /* ... */ }
// This works because Class<Collection<?>> is a valid substitute for Class<? super T>
public Class<Collection<?>> getElementsClass() {
return (Class<Collection<?>>) Collection.class; // Maybe the cast isn't needed
}
}
As for obtaining the elements class, you can use reflection : If your type derives MyElementProcessor you can obtain it like this :
for(Type interface : getClass().getGenericInterfaces()) {
if(interface instanceof ParameterizedType && ((ParameterizedType) interface).getRawType == MyElementProcessor.class)
Type myElementsType = ((ParameterizedType) interface).getActualTypeArguments()[0];
This only works for deriving classes, that is, anonymous classes or declared types, it won't work if you use it this way because of type erasure( this example is dummy : it is only an example) :
public <T> MyElementProcessor newInstance() {
return new MyElementProcessor<T> {
// overridden methods ...
};
}
In such a case, you will either :
Not obtain a ParameterizedType but rather directly the MyElementProcessor.class, whose type argument is a TypeVariable and does not provide you with it's actual type implementation
Obtain a ParameterizedType whose raw type is MyElementProcessor and actual type argument be a TypeVariable
Related
How is Java unable to interpret this code:
private <T> Class<T> getClass(T object) {
return object.getClass();
}
I'm trying to visualise this and when I substitute in for an exmaple
private Class<String> getClass(String s) {
return s.getClass();
}
I get the same sort of error: required type <String> provided type <? extends String>. I've seen code that gets around this error by doing:
#SuppressWarnings("unchecked")
Class<T> clazz = (Class<T>) object.getClass();
What does this mean? What am I not understanding about this generics problem?
getClass follows some specific rules that are a bit unusual. The declared return type of Object::getClass is simply Class<?>, i.e. a class of some type. But as per the docs
The actual result type is Class<? extends |X|> where |X| is the
erasure of the static type of the expression on which getClass is
called.
The erasure for T in your example is simply Object, so the return is Class<? extends Object>. Everything extends Object, so that's the same as Class<?>.
When the parameter is the specific type String, the erasure is also String, and the result is Class<? extends String>.
Oracle tutorial on type erasure
I guess what happens under the hood is that the compiler effectively "overrides" Object::getClass with a more specific return type. Child classes are allowed to return more specific types than their parents, but not less specific. Consider this valid code:
class Foo {
public Class<? extends List<?>> foo() { return null; }
}
class Bar extends Foo {
#Override
public Class<? extends ArrayList<?>> foo() { return null; }
}
Because of how this is implemented, T obj; obj.getClass(); will return Class<?>, rather than what you're expecting which is Class<? extends T>.
In your case, the cast is safe.
As types are still erase you just have an Object object.
Class<?> type = object.getClass();
It is the other way around, one may use:
private <T> T getObject(Object object, Class<T> type) {
return type.cast(object);
}
The above does a dynamic runtime cast. This might be useful:
private final Map<Class<?>, Object> singletonsByType = new HashMap<>();
public <T> void store(Class<T> type, T object) {
singletonsByType.put(type, object);
}
public <S> S lookup(Class<S> type) {
return type.cast(singletonsByType.get(type));
}
You can salve that problem like this:
private <T> Class<T> getClass(T object) {
return (Class<T>)object.getClass();
}
How can I convert java.lang.reflect.Type to Class<T> clazz?
If I have one method as next which has an argument of Class<T>:
public void oneMethod(Class<T> clazz) {
//Impl
}
Then another method which has an argument of java.lang.reflect.Type and it calls oneMethod(Class<T> clazz) and for it I need to convert java.lang.reflect.Type type to Class<T>:
public void someMehtod(java.lang.reflect.Type type) {
// I want to pass type arg to other method converted in Class<T>
otherMethod(¿How to convert java.lang.reflect.Type to Class<T>?);
}
Is it possible?
You have to ensure that type is an instance of Class, and then cast it.
if (type instanceof Class) {
Class<?> clazz = (Class<?>) type;
otherMethod(clazz);
}
Of course, you also have to handle the case of it not being a Class.
If you are willing to use a library, you could use com.google.guava:guava:12+:
Class<?> clazz = com.google.common.reflect.TypeToken.of(type).getRawType();
Alternatively you could also use com.fasterxml.jackson.core:jackson-databind:2.8.x:
Class<?> clazz = com.fasterxml.jackson.databind.type.TypeFactory.rawClass(type);
This handles all cases correctly and you will get the type-erased class of your type.
Using generic types in runtime is a little bit tricky in Java. And I think this is a root cause of your issue.
1) to be sure about generic in runtime we doing like this:
class MyClass<E> {}
and then:
MyClass<TargetType> genericAwaredMyClassInctance = new MyClass<TargetType>(){};
please pay attention to {} in the end. It means anonymous extends of MyClass. This is an important nuance.
2) let`s improve MyClass to be able to extract the type in runtime.
class MyClass<E> {
#SuppressWarnings("unchecked")
protected Class<E> getGenericClass() throws ClassNotFoundException {
Type mySuperclass = getClass().getGenericSuperclass();
Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0];
String className = tType.getTypeName();
return (Class<E>) Class.forName(className);
}
}
and finally, use it like this:
MyClass<TargetType> genericAwaredMyClassInctance = new MyClass<TargetType>(){};
assert(genericAwaredMyClassInctance.getGenericClass() == TargetType.class)
Andy Turner answer is correct, however if you need to retrieve a class from a type parameter this is a complete example:
private static class MyClass extends ArrayList<Integer> {
}
public static void main(String[] args) {
ParameterizedType arrayListWithParamType
= (ParameterizedType) MyClass.class.getGenericSuperclass();
Type integerType = arrayListWithParamType.getActualTypeArguments()[0];
Class<?> integerClass = (Class<?>) integerType;
System.out.println(integerClass == Integer.class);
}
It would be weird that a Type would be anything else than a Class... Javadoc for Type says
All Known Implementing Classes:
Class
So unless you have special libraries that use non Class Types, you can simply cast - but you must be ready for a possible ClassCastException.
Beware: Java use undocumented Type implementation to represent generics, see below.
You can explicitely process it or not because it is an unchecked exception:
Explicit way:
try {
Class<?> clazz = (Class<?>) type;
}
catch (ClassCastException ex) {
// process exception
}
Implicit way:
Class<?> clazz = (Class<?>) type;
but the current method could throw...
EDIT per #Andy Turner's comment:
Beware: Type type = new ArrayList<String>().getClass().getGenericSuperclass(); yields something that's a Type but not a Class. This one is a ParameterizedType, so you can use getRawType() method to find the actual class, but others might exist.
Did you mean this?
public <T extends Type> void oneMethod(T clazz) {
}
public void someMethod(Type type) {
oneMethod(type);
}
I have a method createFoo() that creates instances of Foo<T> using the Class<T> instance for that T. Now I want to extend that method to forward calls that are made using an enum type to the method createEnumFoo(). Calling the second method from the first one seems to be non-trivial. Below is an example of how I managed to do it using two unchecked casts and an extra method, all of which I would like to get rid of.
The method castEnumType() is required because I couldn't find a way to cast a Class<?> to a Class<E extends Enum<E>> without having the E bound somewhere. This involves an unchecked cast because I have not found a way to do it using Class.asSubclass(). After creating the instance of Foo, I need to cast it from Foo<E> to Foo<T> event though E and T will always be the same types.
I can't weaken the signature of createEnumFoo() because it is calling Enum.valueOf(enumType, ...) and requires the result of this to be of type E.
final class Example {
<E extends Enum<E>> Foo<E> createEnumFoo(Class<E> enumType) {
// This makes use of e.g. Enum.valueOf(enumType, ...).
return null;
}
<E extends Enum<E>> Class<E> castEnumType(Class<?> enumType) {
return (Class<E>) enumType;
}
<T> Foo<T> createFoo(Class<T> type) {
if (Enum.class.isAssignableFrom(type))
return (Foo<T>) createEnumFoo(castEnumType(type));
else
// Here we would do something else or maybe throw an exception.
return null;
}
interface Foo<T> {
}
}
Is there a simpler way to do this?
Some context
To clarify the problem I'm facing, I'll explain how this problem actually arose in a project I'm working on:
In the code where I came across this problem, Foo<T> is actually Converter<T>, which is an interface which allows an instance of T to be serialized and de-serialized from and to a JSON value:
public interface Converter<T> {
JsonObject encode(T value);
T decode(JsonObject data);
}
And createFoo() is actually a method converterForType() which takes a Class<T> instance and dynamically dispatches to a bunch of static methods and fields that create/contain converters for common Java types and types specific to the project. Normally when a converter is needed, the appropriate method/field is accessed directly but there are some places where the type is only known at runtime, which is where converterForType() is used.
Now I wanted to extend that method to automatically handle enum types by converting those to JSON strings containing the name of the enum constant. This is why I need to call the method enumConverter() from converterForType(). This is the implementation of enumConverter():
public static <E extends Enum<E>> Converter<E> enumConverter(final Class<E> enumClass) {
return new Converter<E>() {
public JsonObject encode(E value) {
return Json.convert(value.name());
}
public E decode(JsonObject data) {
return Enum.valueOf(enumClass, data.asString());
}
};
}
What about this, use raw types for createEnumFoo method
Edit: fixed compile error reported by #Feuermurmel in comments
#SuppressWarnings({ "unchecked", "rawtypes" })
final class Example
{
<E extends Enum<E>> Foo<E> createEnumFoo(Class enumType)
{
// This makes use of e.g. Enum.valueOf(enumType, ...).
Enum x = Enum.valueOf(enumType, "x");
return (Foo<E>) x;
}
<T extends Enum> Foo<T> createFoo(Class<T> type)
{
if (Enum.class.isAssignableFrom(type))
return (Foo<T>) createEnumFoo(type);
else
// Here we would do something else or maybe throw an exception.
return null;
}
interface Foo<T>
{
}
}
If I have a method, for instance
public INode getNode(final int offset);
I assume it doesn't add something to make the method return type generic, for instance:
public <T extends INode> T getNode(final int offset);
Or did I miss something? I think generic return types are only of value if one of the parameters is of the same type (or a super/subtype)?
public <T extends INode> T getNode(final int offset);
Not only does this not provide any additional information to the caller, but is outright dangerous: The only way to implement that method signature is to use an unchecked cast, which can not be type safe, because a method's type parameters are specified by its caller (explicitly or implictly through type inference), and the type parameters aren't available to this method's implementation. For instance, consider the following program:
class NodeCollection {
private INode[] nodes = new INode[42];
public <T extends INode> T getNode(final int offset) {
return (T) nodes[offset];
}
public <T extends INode> setNode(final int offset, T node) {
nodes[offset] = node;
}
}
class ANode implements INode {}
class BNode implements INode {
void foo();
}
public class Test {
public static void main(String[] args) {
NodeCollection nc = new NodeCollection();
nc.setNode(0,new ANode());
BNode b = nc.getNode(0); // throws ClassCastException (sic!)
}
}
Best practice: Don't use an unchecked cast, unless you are really sure it'll be type correct at runtime.
I think generic return types are only of value if one of the parameters is of the same type (or a super/subtype)?
There are more cases, for instance:
public <T> T getFavorite(Class<T> clazz) {
return clazz.cast(favorites.get(clazz));
}
or
interface List<E> {
E get(int index);
}
or the examples in Colin's answer, where the type variable merely appears as type parameter in the return type, which is acceptable due to type erasure.
Edit:
I think there's no type save way if one wants to cast to the exact type of node (instead of instanceof has to precede it)
Of course there is, it's called visitor pattern.
In the example you give, it doesn't really add any value except forcing the developer of the implementation to cast the return value (in T). (Unless it has a way to get a T value, but for that it would need to call another method which returns T, so you're just moving the cast a bit further.
So not really a good idea in the case you're showing us.
There are a few cases where the generic applied only to the return value can be useful. For example:
public static <T> Collection<T> generateCollection() {
return new ArrayList<T>();
}
This allows you to create an object Collection of T without having to do any cast.
Or if you want the developer doing the implementation to cast his object (mostly works when the said object uses generics), example from Collections:
public static final <T> Set<T> emptySet() {
return (Set<T>) EMPTY_SET;
}
Resources:
angelikalanger.com: Java Generics FAQs - Generic Methods
I have a generic class, says :
MyClass<T>
Inside a method of this class, I would like to test the type of T, for example :
void MyMethod()
{
if (T == String)
...
if (T == int)
...
}
how can I do that ?
Thanks for your help
You can't, normally, due to type erasure. See Angelika Langer's Java Generics FAQ for more details.
What you can do is pass a Class<T> into your constructor, and then check that:
public MyClass<T>
{
private final Class<T> clazz;
public MyClass(Class<T> clazz)
{
this.clazz = clazz;
}
public void myMethod()
{
if (clazz == String.class)
{
...
}
}
}
Note that Java does not allow primitives to be used for type arguments though, so int is out...
Because of type erasure you can't... mostly. But there is one exception to that. Consider:
class A {
List<String> list;
}
public class Main {
public static void main(String args[]) {
for (Field field : A.class.getDeclaredFields()) {
System.out.printf("%s: %s%n", field.getName(), field.getGenericType());
}
}
}
Output:
list: java.util.List<java.lang.String>
If you need the class object, this is how you generally handle it:
public <T> T createObject(Class<T> clazz) {
return clazz.newInstance();
}
ie by passing the class object around and deriving the generic type from that class.
Additionally to cletus one exception I've mine: super type tokens. The super type token will preserve the type information.
new Type<Set<Integer>>() {}
The type information can be retrieved with Class.getGenericSuperClass.
if (object instanceof String)
System.out.println("object is a string");
As it was already stated you can get only generics-related information available at the static byte code level.
It's possible to resolve type arguments values and check if one type may be used in place of another then.
if you have subclass B extends A that should match, too, the approach clazz == A.class. Doesn't work. You should then use A.class.isInstance(b) where b is an object of type B.
If you want to do different things for different types would it still be generic?