1

I'm guessing there is absolutely no way I can do something like:

Class c = Class.forName("Processor<Integer, String>");

in Java? (where I defined Processor previously, of course).

Frank
  • 4,341
  • 8
  • 41
  • 57
  • 3
    I think the better question here, is why are you thinking you need to do this? Perhaps, there is a much easier way not involving reflection. – thatidiotguy Sep 21 '12 at 20:06
  • OK, I do have a potential use case, which probably has other solutions. I do have a Processor class with functionality I want to reuse for different Input, where Input is generated from an Avro schema. I want to call the same Processor with different, compatible Input schemas. – Frank Sep 21 '12 at 20:08
  • There *might* be some way to accomplish something like this via the [reflection API](http://docs.oracle.com/javase/tutorial/reflect/index.html), but it's not clear that is going to be simple. Can you spell out more clearly why Java's inheritance model (and polymorphism, or something else) doesn't cover whatever it is you're trying to do? Also, can you go into your problem with some more detail so we can understand your goal? – jefflunt Sep 21 '12 at 20:09
  • Yes, inheritance between the various Input types would be nice, but I don't have it. Part of it is legacy. Another reason is that we are supposed to support both primitive types and more elaborate types generated from Avro schemas, and we don't want to wrap every int/String... . Also, we already have a configuration mechanism for the Processors, and it would be really sweet if users could reuse the same processor logic but just vary the actual (compatible) input type in the configuration file. – Frank Sep 21 '12 at 20:17
  • possible duplicate? http://stackoverflow.com/questions/6373800/use-reflection-to-create-a-generic-parameterized-class-in-java?rq=1 – Saintali Sep 22 '12 at 21:51

2 Answers2

2

Absolutely no way as generic arguments can exist only at compile time. Class object is the same on runtime. It is not class template as in C++. Type parameters are just information for java compiler.

You can try to accomplish something similar by creating a class :

class IntStringProcessor extends Processor<Integer, String> {}

and Class c = Class.forName("IntStringProcessor");

On runtime you can get actual Processor type parameters via c.getGenericSuperclass() but this extends the question I guess.

EDIT: Another idea

Your Processor may keep classess as parameters and then:

Class c1 = Class.forName("java.lang.Integer");
Class c2 = Class.forName("java.lang.String");
Processor = new Processor(c1,c2);

Your processor is now not generic but uses Class instances inside. As I said, there is no way to create generic instance on runtime because generics exist only at compile time.

Piotr Gwiazda
  • 12,080
  • 13
  • 60
  • 91
  • Yes, but I want the forName to be driven from a configuration file, so I don't want to declare IntStringProcessor :-) – Frank Sep 21 '12 at 20:18
  • So "wrong way" is the answer. I guess you have an error somwhere in your design that lead you to creating class instances from configuration files. – Piotr Gwiazda Sep 21 '12 at 20:20
  • 1
    No, that is not farfetched. Why should I not be able to create new types dynamically from a configuration file? – Frank Sep 21 '12 at 20:22
  • I am not sure what you mean. You want to create a new instance of java.lang.Class. The type must exist already in your jars. To add new type you still need to recompile your cone an redeploy unless you use OSGi or custom classloader that loads classes from somwhere. I guess you should try to use composition over inheritance. – Piotr Gwiazda Sep 22 '12 at 10:15
0

First let me straighten something. There is no Class object in Java for Processor<Integer, String>. There is only one Class object for Processor, namely Processor.class and all generic instantiations share the same Class object:

    Processor<Integer, String> x = new Processor<Integer, String>();
    Processor<Character, Byte> y = new Processor<Character, Byte>();
    System.out.println(x.getClass() == y.getClass());        // prints true

Having said that, if all you need is to have tokens for generic types to pass around, you can use a third party implementation. Here I use Java ClassMate:

    TypeResolver typeResolver = new TypeResolver();
    // type token for Processor<Integer, String>
    ResolvedType type = typeResolver.resolve(Processor.class, Integer.class, String.class);
    System.out.println("type = " + type);

If you read names of Java types as String's from a file, you probably want to use Class.forName() instead of my .class literals.

You'll have to parse <, > brackets yourself, but that should be easy, as those single characters always mean the same thing in Java type names...

You can form nested parameters like this:

    // type token for Processor<Processor<Integer, String>, Character>
    ResolvedType type = typeResolver.resolve(Processor.class,
            typeResolver.resolve(Processor.class, Integer.class, String.class),
            typeResolver.resolve(Character.class)
    );
    System.out.println("type = " + type);

If you want to create type tokens for types you know at compilation time, you can use the so-called super-type-token pattern. Just put any type name you need inside new GenericType<...>() {}:

    type = typeResolver.resolve(new GenericType<Processor<Processor<Integer, String>, Character>>() {});
    System.out.println("type = " + type);
Saintali
  • 4,482
  • 2
  • 29
  • 49