Given a generic class Foo<T>, I would like to create a static method valueOf(String s). The String s should hold the name of a class (e.g. "java.lang.Integer"). A call to valueOf("java.lang.Integer") should then return a new object Foo<Integer>.
In my class (see code below), I want to store the Class<T>, which is passed as parameter to the constructor Foo(Class<T> clazz). But in the valueOf-method, I am not sure what parameter to pass to the constructor Foo(Class<T> clazz) of the object I like to return.
Since I am relatively new to java reflections, I am unsure how I can implement this logic to my generic class. I have learned that the Class.forName(String) method returns an object Class<?>, but I don't know how to find out the type that the wildcard ? stands for (in case that's possible using reflections), or am I wrong going into this direction?
public class Foo<T> {
private Class<T> clazz;
public Foo(Class<T> clazz) {
this.clazz = clazz;
}
public static <E> Foo<E> valueOf(String s) throws ClassNotFoundException {
Class<?> clazz = Class.forName(s);
// here i am stuck. I would like to return new Foo<E>,
// but what is E, E's class and therefore the parameter for the constructor?
}
}
You can't do this with generics. Generics are just casts the compiler puts in for you, statically. So, if you couldn't do this with casts you write in the code (that is, you can't have a different cast for different strings), you can't do it with generics either.
You need some sort of key instead of a String which conveys the type T/E. You can pass in the Class<E> directly:
public static <E> Foo<E> valueOf(Class<E> clazz) throws ClassNotFoundException {
// ...
}
Related
I am trying to write a method where I can convert from a string to an enum object at runtime, for a generic enum. I have a method signature:
public static <T extends Enum<T>> Enum<T> foo(String string, Class<T> clazz)
However, I am calling it from a class whose generic type parameter does not explicitly extend Enum. i.e.
class bar<X> {
private Class<X> clazz;
if (XIsAnEnum()) {
foo(string, clazz)
}
}
This does not compile because even though I know, from the logic of XIsAnEnum, that X extends Enum<X>, I don't explicitly state this in the generic type parameter definition, so it is not a valid argument.
Is there a way to do an unchecked cast from Class<X> to Class<X extends Enum<X>>, or will I have to make a new class bar2<X extends Enum<X>> specifically for when I want to use enums?
You can use Class#asSubclass(Class) to do the cast for you, like
foo("value", clazz.asSubclass(Enum.class));
This involves an actual verification that clazz is referring to a Class that is a subclass of Enum.
You're throwing out all the generic verification here though.
I have a generic method and would like to retrieve objects using the generic type. This is my method:
public static <T extends RealmObject & IndentifierModel> void storeNewData() {
...
T item = realm.where(Class<T>) // Not compiling (Expression not expected)
.equalTo("ID", data.get(i).getID())
.findFirst();
}
The above isn't working for realm.where(Class<T>). How do I pass in my generic type to Realm?
You have to supply the generic parameter like so:
public static <T extends RealmObject & IndentifierModel> void storeNewData(Class<T> clazz) {
T item = realm.where(clazz)
.equalTo("ID", 123)
.findFirst();
}
Class<T> is not valid, since that's like saying realm.where(Class<List<String>>) or realm.where(Class<String>). What you need is an actual Class<T> instance. But you cannot use T.class either since T is not available at runtime due to type-erasure. At runtime, the method basically needs a Class<T> instance to work properly. Since you cannot get that from T, you will have to explicitly supply an argument of type Class<T>.
Let's say I have the following class:
public class MyClass<T> {
Class<T> type;
List<T> items;
public Class<T> getType() { return type; }
...
}
I need to store objects of the above class in a generic container:
public class MyContainer<T> {
Class<T> type;
List<MyClass<T>> myClasses;
public Class<T> getType() { return type; }
...
}
Let's say later in my code I'm given a generic object as an argument:
public class Main {
public static void main() {
List<MyContainer<?>> containers;
...
}
public void someMethod(MyClass<?> myClass){
...
}
}
I have type T as reflection. I need to cast MyClass<?> back to its intended type. So if it was declared as MyClass<String> I need to cast it back to MyClass<String>. I can't seem to do this with the reflection type because it is only capable of casting T, not MyClass<T>.
I could cast T by doing this for example:
myClass.getType().cast(myClass.getItem()); // Where getItem returns an object of the type T
But what I want is this:
myClass.getType().cast(myClass); // getType() returns T not MyClass<T>
So the question is, how do I cast a generic base type along with its reflection type, given the scenario above?
This is impossible due to type erasure. Reflection can't help you here because at runtime the generic type information isn't available. In other words at runtime there is no MyClass<String>, there is only MyClass. MyClass<String> is a pure compile-time creature.
If you have some piece of code that expects a certain type, declare that at compile time. This type expectation could be concrete or generic:
public void someMethod(MyClass<String> myClass)
or
public <T> void someMethod(MyClass<T> myClass)
If you know the type, you don't need cast(), you can just cast:
#SuppressWarnings("unchecked")
MyClass<String> myClassStr = (MyClass<String>)expression;
Since T doesn't exist at runtime, which you know, you can't use it. You also can't get a Class<List<T>>.
myClass.getType().cast(myClass.getItem()) doesn't really work for you because if you have a MyClass<?> then you also have a Class<?> which only returns Object. A Class<T> doesn't really help you either, because you need to actually know what T is.
You can pretty much do two things.
Use decisions:
if(myClass.getType() == String.class) {
#SuppressWarnings("unchecked")
final MyClass<String> myStrings = (MyClass<String>)myClass;
// use MyClass<String> ...
}
Use polymorphism:
MyClass<String> myClass = new MyClass<String>(...,
new BiConsumer<String, JTable>() { // (or lambda ...)
#Override
public void accept(String s, JTable table) {
// use String ...
// configure JTable ...
}
});
... elsewhere ...
public void someMethod(MyClass<?> myClass) {
someCaptureMethod(myClass);
}
private <T> void someCaptureMethod(MyClass<T> myClass) {
Consumer<T, JTable> consumer = myClass.getConsumer();
for(T theT : myClass.getList()) {
consumer.accept(theT, myTable);
}
}
Generally the second example is considered the "better" way but the code might be longer in versions of Java prior to 8.
I have a method whose signature is:
public static <T> T isA(Class<T> clazz);
So I can do this:
String str = isA(String.class);
Where I'm having trouble is if I want T to be Class<String>:
Class<String> cls = isA(???);
I'm not sure how to formulate the argument to isA(). Can anyone offer guidence?
In case you're wondering why I want to do this, I'm using EasyMock to mock a class that takes a Class<T> argument.
EDIT: I was asked to add an example of what I'm trying to do.
I'm trying to use EasyMock to mock Solr's SolrCore class as part of a test case. The signature of one of SolrCore's methods is:
public <T extends Object> T createInitInstance(PluginInfo info, Class<T> cast, String msg, String defClassName);
With EasyMock I can set up an expectation for that method. The construct isA(PluginInfo.class), for example, tells EasyMock to match any object of class PluginInfo:
QueryParserPlugin queryPlugin = createNiceMock(QueryParserPlugin.class);
SolrCore core = createNiceMock(SolrCore.class);
expect(core.createInitInstance(
isA(PluginInfo.class), isA(xxx),
anyString(), anyString())).andReturn(queryPlugin);
My problem is telling isA() to match any object of class Class<T>, where T in this case is QueryParserPlugin.
The issue with reflection and java is type erasure. You just need to give the compiler a hint.
Since the object you are expecting is of type T, and the method itself is generic, you kinda have to
let java know what type you really are working with.
All it needs is a bit of a hint, something during runtime that can be passed through a compiled method that holds that type information.
So at compile time you have a method that takes in:
Class<String>
the compiler only knows the compiled type, so has no clue that the type itself is a class definition, making it impossible to assign if you don't tell java what the type of the assignment is.
So this works:
Class<String> myVar = String.class;
Or this works:
Class<String> myVar = isA(String.class);
Or this works
public <T> T myMethod(Class<T> object)
Class<String> class = myMethod(String.class)
but this doesn't work
public <T> void myMethod(Class<T> object);
since we have no assignment of T for the generic.
so how do you let the compiler know that T really is a class?
public <T> void myClassWrapper(Class<? super T> object);
myMethod(myClassWrapper(String.class));
so by passing it through a method that accepts you let the compiler know that at minimum that this thing is a class and that it represents T at some part of T's own hierarchy, thus letting the method compile.
or of course you could always just do
myMethod((Class<String>)string.class));
but I think thats kinda hackish personally. I am not a fan of casts that are not explcit and wrapped in a method.
Since you cannot control the signature of the test framework, you can however let java know your intentions.
I am not sure how easy mock works, but heres a test to kinda help explain whats going on.
#Test
public void testCreation(){
Object integer = 5;
String myString = "A String";
int five = typeTheObject(Integer.class, integer);
Class<String> stringClass = typeTheObject(myString);
Class<Integer> myInt = typeTheObject(five);
Class<?> myClass = typeTheObject(String.class);
TypeValidator typeValidator = new TypeValidator(stringClass);
typeValidator.isA(typeTheObject(String.class));
}
public static class TypeValidator{
private final Object objectToValidate;
public TypeValidator(Object object){
objectToValidate = object;
}
public <T> T isA(T type){
if(objectToValidate.getClass().isAssignableFrom(type.getClass())){
return type;
}else{
Assert.fail();
return null; //cuase
}
}
}
public static <T> Class<T> typeTheObject(Class<? super T> type){
return (Class<T>)type;
}
public static <T> T typeTheObject(Class<T> type, Object object){
if(object.getClass().isAssignableFrom(type)){
return (T)object;
}
return (T)object;
}
public static <T> Class<T> typeTheObject(Object object){
return (Class<T>)((T)object).getClass();
}
Though one big drawback is paramaterized types. But those can be solved using a guice type literal.
(new TypeLiteral<List<String>(){}).getRawType();
since its annon the type holds during runtime.
I have an abstract class called Data, with a getInstance() method which should return instances of concrete subclasses of Data.
I want to pass a String to the getInstance method, and this string will define exactly what class will be returned.
So far I have:
public abstract class Data {
private Map<String, Data> instances;
public <T extends Data> T getInstance(Class<T> type, String dataName) {
return type.cast(instances.get(dataName));
}
}
Where the getInstance() method looks up the correct instance in the instances map. I think this should be OK provided the map is populated (by Spring), but the caller must match the Class<T> type parameter with the String dataName parameter.
Is there any way I can remove the Class<T> type parameter and not have generics warnings?
No, you will always get a warning if you try to cast into a generic type without knowing the runtime class of the type. And no, there is no way to get an instance of the generic class without providing it as an argument - generics are erased at runtime.
The only way to get no warnings without an explicit cast is to return either Object or Data (depending on you Map) and require the user to make the cast instead:
public Data getInstance(String dataName) {
return instances.get(dataName);
}
// OR
public Object getInstance(String dataName) {
return instances.get(dataName);
}
In my opinion it is best, to provide both methods for convenience: Data getInstance(String) and <T extends Data> T getInstance(Class<T>, String). This is essentially also what OSGI does in the BundleContext class, so one is able to get service references, with the difference, that it is not possible to get services with arbitrary ids (the id is always the name of the class):
ServiceReference<?> BundleContext#getServiceReference(java.lang.String clazz)
ServiceReference<S> BundleContext#getServiceReference(java.lang.Class<S> clazz)
You can skip the class parameter altogether.
public <T extends Data> T getInstance(String dataName) {
return (T)instances.get(dataName);
}
This will still generate a warning (which you can suppress). Both ways will throw a runtime exception if actual class in the map differs from expected type. In my example compile type inferrence will not work in certain cases and you will need to specify type when calling like this: Data.<SubClass>getInstance("name");
It is possible to have a solution which will return null if the subtype of data for the key is not correct.
public <T extends Data> T getInstance(Class<T> clazz, String dataName) {
Data d = instances.get(dataName);
if (d != null && clazz.isAssignableFrom(d.getClass())) {
return (T)d;
} else {
return null;
}
}
This code will return null if the value in the map is not of correct class T or its subclass.