I am tying to save the Type of a generic value, because i can't get it at runtime:
public class A<T> {
private final Class<T> genericType;
public A(Class<T> genericType) {
this.genericType = genericType;
}
public Class getGenericType() {
return genericType;
}
}
To make subclasses now, I use it as follows:
public class B extends A<String> {
public B() {
super(String.class);
}
}
Note thet the super()'s parameter type matches (by compile timne check) to the A's generic type.
That works fine. But if i want to have it with a Map, i cannot get the correct class object:
public class C extends A<Map<String, String>> {
public C() {
super(Map.class); // does not match Map<String,String>
super(Map<String,String>.class) // no valid java expression, i dont know what
}
}
Sooo anyone got a tip to help me out of this misery?
Best i could do currently, is to give up the strong typing in A:
public class A<T> {
// old: private final Class<T> genericType;
private final Class genericType; // note the missing generic
public A(Class genericType) { // here as well
this.genericType = genericType;
}
public Class getGenericType() {
return genericType;
}
}
I am not sure whether this satisfied your requirement, but you can do something similar as bellow, see How to get the class of a field of type T?
import java.lang.reflect.*;
import java.util.*;
public class GenericTypeTest{
public static void main(String []args){
B b = new B();
System.out.println("B is a " + b.getGenericType());
C c = new C();
System.out.println("C is a " + c.getGenericType());
}
}
class A<T> {
public Class getGenericType() {
Object genericType = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
if(genericType instanceof ParameterizedType){
genericType = ((ParameterizedType)genericType).getRawType();
}
return (Class<T>) genericType;
}
}
class B extends A<String> {
}
class C extends A<Map<String,String>> {
}
this will get output something like
B is a class java.lang.String
C is a interface java.util.Map
There is just one Class object representing Map at runtime, which you can get by evaluating Map.class. There are no separate Class objects representing Map<String, String> or Map<Integer, Integer> or whatever at runtime. If you just want to take Map.class, which normally has type Class<Map>, and force that into Class<Map<String, String>>, you could do that via some unchecked casts:
super((Class<Map<String, String>>)(Class<?>)Map.class);
But whether that would do what you want depends on what you expect to do with your variable of type Class<T>, genericType, which you have not shown. For example, if you will use its .isInstance() method to check whether an object is an instance of T at runtime, know that since we don't know the generic type arguments of objects at runtime, we will only be able to check the raw type of the object, and not its type argument. This is the same reason why you can do foo instanceof Map or foo instanceof Map<?> but not foo instanceof Map<String, String>.
Or, maybe you want to use its .cast() method to do a type check on an object at runtime, which normally throws an exception if the object is not an instance of the Class's class, and if it is, it returns the object, but with compile-time type T. But here, again, you can't check at runtime whether an object is an instance of a parameterized type like Map<String, String>; you can only check whether it is a Map. So by it allowing you to get the result as type T without warning might be unsafe, as you get a compile-time expression of type Map<String, String>, but it might really be a Map<Integer, Integer>. This is the same reason why an unchecked cast like (Map<String, String>)foo causes an unchecked cast warning. It is unsafe and might cause unexpected problems elsewhere in your code.
Related
I am trying to define a class which type could be a subtype of the given one if inferred but it doesn't seem to work with the default Java type inference mechanism and I do not understand why.
Here are some relevant pieces of code to illustrate the situation
public class ObjectChanged<T extends Something> implements TriggeringCondition<T> {
private final Class<? extends T> type;
private String featureName = null;
protected ObjectChanged(Class<? extends T> type) {
this.type = type;
}
public ObjectChanged<T> onFeature(String featureName) {
this.featureName = featureName;
return this;
}
public static <X extends Something> ObjectChanged<X> objectChanged(Class<? extends X> type) {
return new ObjectChanged<>(type);
}
}
Let's say I have one class called FastCar extending Car. I would like to build an object change for a FastCar, but to downcast it to TriggeringCondition<Car>.
If I write the following code it works as expected
TriggeringCondition<Car> test() {
return objectChanged(FastCar.class);
}
But then if I call the onFeature(String) method it doesn't compile anymore and complains that my triggering condition if of type FastCar, which is not compatible with Car.
If now I define the objectChanged function like this
public static <X extends Something, Y extends X> ObjectChanged<X> objectChanged(Class<Y> type, Class<X> baseType) {
return new ObjectChanged<>(type);
}
Then I can use this code which resolves the problem
TriggeringCondition<Car> test() {
return objectChanged(FastCar.class, Car.class).onFeature("something");
}
I also found out I can fix the previous build issue with this syntax, but it's quite ugly imo.
TriggeringCondition<Car> test() {
return ObjectChanged.<Car> objectChanged(FastCar.class).onFeature("test");
}
Is there a way to write the test method like this without needing an extra parameter ?
TriggeringCondition<Car> test() {
return objectChanged(FastCar.class).onFeature("test");
}
Is there a way to write the test method like this without needing an extra parameter ?
No.
If you don't want to use the type witness (<Car>), all you can do is to assign the objectChanged result to a variable, and then call onFeature on that variable.
TriggeringCondition<Car> test() {
TriggeringCondition<Car> tc = objectChanged(FastCar.class);
return tc.onFeature("test");
}
This is a problem which crops up a lot if you use Guava's Immutable*.Builders:
ImmutableList<String> list =
ImmutableList.<String>builder()
.add("foo")
.build();
The type witness is needed here, otherwise the type of the Builder is inferred to be ImmutableList.Builder<Object>, because the type of the polyexpression is determined before the .add(String) call.
It's annoying, but that's the nature of the beast.
One thing you could do is to define a static upcast method:
static <T extends Something> ObjectChanged<T> upcast(ObjectChanged<? extends T> oc) {
ObjectChanged<T> result = new ObjectChanged<>(oc.type);
return result.onFeature("test");
}
Now you can invoke something like:
TriggeringCondition<Car> test() {
return upcast(objectChanged(FastCar.class).onFeature("test"));
}
I have a generic type that implements a generic interface. Java compiler correctly infers the constraint in the type parameter of the type in all cases but fails to do so in one.
See getValue() method implementation in Generic class below:
package test.gen.company;
public class GenericProblem {
Generic<ISpecific> gen = new Generic<>();
//compiler has no problems here
ISpecific val = gen.getValue();
Generic<IMoreSpecific> gen2 = new Generic<>();
//both of these are OK
ISpecific val1 = gen2.getValue();
IMoreSpecific val2 = gen2.getValue();
//compiler won't allow this,
//correctly complains irrelevant is not within bounds
//Generic<Irrelevant> gen;
}
interface IGeneric<T>{
T getValue();
void setValue(T in);
}
interface ISpecific {}
interface IMoreSpecific extends ISpecific {}
interface Irrelevant{}
class ISpecificImplementer implements ISpecific {}
class Generic<T extends ISpecific> implements IGeneric<T> {
#Override
public T getValue() {
//error: required T, found ISpecificImplementer
return new ISpecificImplementer();
//Unchecked cast
//return (T) new ISpecificImplementer();
}
#Override
public void setValue(T in) {
//no complaints here, compiler knows T is ISpecific
wantSomeB(in);
}
private void wantSomeB(ISpecific in){
if (in == null) {
throw new RuntimeException("I'm completely meaningless");
}
}
}
The compiler sets the type of parameter in setValue(T in) to T extends ISpecific based on the type parameter of Generic but it cannot set the type of T in T getValue()
When I use a cast to T in getValue() it then complains about an unchecked cast.
Why is type inference working for the setter but not for the getter?
T in Generic<T extends ISpecific> is allowed to be any type extending ISpecific.
That means it could be ISpecificImplementer, or it could be some other matching type. The choice of T isn't up to the method to decide; it's up to whatever creates the Generic<T> instance.
Inside getValue(), you're trying to return an ISpecificImplementer. Now T might be ISpecificImplementer, or it might be some other type that is not compatible. So it requires a cast. And casting to a generic type generates a warning, because it is bypassing the type-safety that generics are intended to provide.
Suppose that SomeOtherType is another class implementing ISpecific.
If you instantiate a Generic<SomeOtherType> and call getValue(), you'll end up with a casting exception, because getValue() should return a SomeOtherType, but it will actually try and return a ISpecificImplementer. That's why there's a compile warning.
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);
}
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
What is the best way to retrieve the runtime value of a generic parameter for a generic class? For example:
public class MyClass<T> {
public void printT() {
// print the class of T, something like:
// System.out.println(T.class.getName());
}
}
So if I call
new MyClass<String>().printT()
it will print "String"
You don't. Due to type erasure that information is (mostly) lost at runtime. If you really need the class this is what you do:
public class MyClass<T> {
private final Class<T> clazz;
public MyClass(Class<T> c) {
if (c == null) {
throw new NullPointerException("class cannot be null");
}
clazz = c;
}
public void printT() {
System.out.println(clazz.getName());
}
}
and then you have access to it.
To achieve that, you need to add the type info, since type erasure means that T's type is not available.
public class MyClass<T> {
private final Class<T> clazz;
public MyClass(Class<T> clazz) {
this.clazz=clazz;
}
public void printT() {
// print the class of T, something like:
System.out.println(this.clazz);
}
}
Java doesn't have that information at runtime, because of type erasure. You would need to pass the type as a constructor parameter to the class when the object is instantiated. There are some libraries and languages which can help you to do that with less typing: Guice can do it and also Scala can do it.
As mentioned before, you can't obtain type information at that example because of type erasure.
However, you can redesign your classes hierarchy in order to have a generic superclass/interface and make child classes directly define type parameter at their definitions:
package com;
import java.lang.reflect.ParameterizedType;
import java.util.Arrays;
public class AAA {
public static void main(String[] args) throws Exception {
Object target = new MyClass<Integer>() {}; // child class that explicitly defines superclass type parameter is declared here
ParameterizedType type = (ParameterizedType) target.getClass().getGenericSuperclass();
System.out.println(Arrays.toString(type.getActualTypeArguments()));
}
}
class MyClass<T> {
}