Java generics question

Hey guys, i know this is not a JME related question but i tought i ask anyway.

I use this code to load plugins.
[java]
ArrayList plugins=new ArrayList();
Reflections reflections=new Reflections(“lwdigital.plugin”);
Set pluginClasses= reflections.getSubTypesOf(LWDPlugin.class);
for(Class pluginClass: pluginClasses){
LWDPlugin pluginInstance= pluginClass.newInstance();
plugins.add(pluginInstance);
}
[/java]

Basically this code searches the package lwdigital.plugin and creates new instances of all classes which extends LWDPlugin
It works good, but since i need to do this in different locations and i don’t want to maintain the same code in different locations i wanted to make a generic loader.

[java]
public class GenericReflectionTest {

String pack;

public GenericReflectionTest(String pack) {
    this.pack = pack;
}

public ArrayList load() throws InstantiationException, IllegalAccessException {
    ArrayList plugins = new ArrayList();
    Reflections reflections = new Reflections(this.pack);
    Set pluginClasses = reflections.getSubTypesOf(T); //Error
    for (Class pluginClass : pluginClasses) {
        T pluginInstance = pluginClass.newInstance();
        plugins.add(pluginInstance);
    }
    return plugins;
}

}
[/java]

The problem is, reflection.getSubTypesOf() expects a Class, and i can’t find a method to get the class of the generic T. (which at compile time is not generic anymore). Is there a solution without having to pass the Class as a parameter? Of even better, transform the whole Class into a static function?

[java]
load(Class-? extends PluginClass- type);
[/java]
perhaps? Are you actually in need of using generics for it?

Edit: Yea forgot code.

Hm, looks like i can’t ‘less-then’ and ‘greater-then’ signs.

Here is the pastebin link which shows the correct code: http://pastebin.com/KZDkbHRG

I think i will definately need generics, i don’t know how to Cast the return type without generics for example

Well you still need to pass a class type to have the type in the method.

Try something like this and work from there:

[java]
public -T- T create(Class-T- clazz) {
try {
//T must have a no arg constructor for this to work
return clazz.newInstance();
} catch (InstantiationException e) {
throw new IllegalStateException(e);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
[/java]

You can use type token pattern. Something like this (where %T% is )

[java]
public abstract class GenericReflectionTest%T% {

String pack;
Class%? extends T% spec;

public GenericReflectionTest(String pack) {
    this.pack = pack;
    this.spec = (Class%T%)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}

public ArrayList%? extends T% load() throws InstantiationException, IllegalAccessException {
    ArrayList%? extends T% plugins = new ArrayList%? extends T%();
    Reflections reflections = new Reflections(this.pack);
    Set%? extends T% pluginClasses = reflections.getSubTypesOf(spec); 
    for (Class%? extends T> pluginClass : pluginClasses) {
        T pluginInstance = pluginClass.newInstance();
        plugins.add(pluginInstance);
    }
    return plugins;
}

}
[/java]

and then instantiating it as

[java]
GenericReflectionTest%MyPluginClass% test = new GenericReflectionTest%MyPluginClass%() {};
[/java]

Please note the extra brackets at the end - they are constructin anonymous subclass of GenericReflectionTest, which is freezing specialization of type parameter in place.

1 Like

Hmh, don’t like the non standart instatiating. But it would work. Thanks for the input

Or, screw all generics give it the class as the argument you search for, and simply cast the returnvalue, (in that place you know what it has to be anyway since you give the parameter)

This is one of the points im a bit annoyed about java, the generics are just very powerless compared to other languages.

Curious, in this case why is passing the class distasteful? Seems like “I’d like to load classes of this interface from this package” is a pretty logical thing to pass to the constructor.

Also, what JVM do you hope to target? Never Java will let the type be inferred even in a constructor if you call it right. Still, for a lot of reasons a factory method can be better and would have this already.

[java]public class GenericReflectionTest<T> {

String pack;
Class&lt;T&gt; type;

protected GenericReflectionTest(String pack, Class&lt;T&gt; type) {
    this.pack = pack;
    this.type = type;
}

public static &lt;T&gt; GenericReflectionTest&lt;T&gt; create( String pack, &lt;T&gt; type ) {
    return new GenericReflectionTest&lt;T&gt;(pack, type);
} 

public List&lt;T&gt; load() throws InstantiationException, IllegalAccessException {
    ArrayList&lt;T&gt; plugins = new ArrayList&lt;T&gt;();
    Reflections reflections = new Reflections(this.pack);
    Set pluginClasses = reflections.getSubTypesOf(type);
    for (Class pluginClass : pluginClasses) {
        T pluginInstance = pluginClass.newInstance();
        plugins.add(pluginInstance);
    }
    return plugins;
}

}[/java]

I’ll admit that I didn’t click through your links so maybe you were parameterizing the load() method. Even simpler then since I think it is logical to pass the type there as well. The type you capture and the type you pass might not even be exactly the same. They don’t have to be, anyway. They just need to be compatible.

[java]List<GenericPlugin> stuff = myLoader.load( MoreSpecificPlugin.class );[/java]

I’ll admit this is the one pet peeve I have on generics but in this case I think passing the type is not so bad. Type erasure is a bitch but it was one way to keep code compatible across VMs and occasionally the erasure-safe idioms are actually a little more robust.

1 Like
@Empire Phoenix said: Or, screw all generics give it the class as the argument you search for, and simply cast the returnvalue, (in that place you know what it has to be anyway since you give the parameter)

This is one of the points im a bit annoyed about java, the generics are just very powerless compared to other languages.

Call me lazy, but if possible i try to avoid having to cast manually. And in this case i am passing the type already once, so if possible i would like to avoid having to write the same TypeDefinition again for casting…

@pspeed said: Curious, in this case why is passing the class distasteful? Seems like "I'd like to load classes of this interface from this package" is a pretty logical thing to pass to the constructor.

Also, what JVM do you hope to target? Never Java will let the type be inferred even in a constructor if you call it right. Still, for a lot of reasons a factory method can be better and would have this already.

I’ll admit that I didn’t click through your links so maybe you were parameterizing the load() method. Even simpler then since I think it is logical to pass the type there as well. The type you capture and the type you pass might not even be exactly the same. They don’t have to be, anyway. They just need to be compatible.

I’ll admit this is the one pet peeve I have on generics but in this case I think passing the type is not so bad. Type erasure is a bitch but it was one way to keep code compatible across VMs and occasionally the erasure-safe idioms are actually a little more robust.

Whoooohooo, that was the push in the right direction.