简体   繁体   中英

How to instantiate a generic class based on class parameters in Java

How do I instantiate a new generic class based on class parameters? My main class looks as follows:

public class ServiceTransformer<SERVICE_I, SERVICE_O> extends Loggable {

private Class<SERVICE_I> serviceInputClass;
private Class<SERVICE_O> serviceOutputClass;

public ServiceTransformer(Logger logger, String inputXslt, String outputXslt, String appRoot) {
    try {
        serviceInputClass = (Class<SERVICE_I>) getGenericTypeClass(0);
        serviceOutputClass = (Class<SERVICE_O>) getGenericTypeClass(1);
    } catch (BadConfigurationException ex) {
        LOGGER.error("@@@ ConfigBase() Constructor Exception => ", ex);
    } catch (MalformedURLException ex) {
        LOGGER.error("@@@ ConfigBase() Constructor Exception => ", ex);
    }
}

private Type getGenericTypeClass(int index) {
    try {
        return ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[index];
    } catch (Exception ex) {
        LOGGER.error("@@@ getGenericTypeClass() Exception => ", ex);
    }
    return null;
}
}

And I instantiate a new ServiceTransformer class as follows:

ServiceTransformer<ReactivatePrepaidSubscriberInput, ReactivatePrepaidSubcriberOutput> service = 
                new ServiceTransformer<ReactivatePrepaidSubscriberInput, ReactivatePrepaidSubcriberOutput>(
                        LOGGER, inputFile, outputFile, APPROOT);

The error that I am currently getting is java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType

No idea where I am going wrong.

It is only possible to get the generic type arguments from the superclass, just as you are doing it:

return ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()

But your ServiceTransformer does not extend a generic class, only Loggable , which is not generic, hence cannot be cast to ParameterizedType .

The trick you could do would be to instantiate an anonymous subclass of ServiceTransformer :

new ServiceTransformer<In, Out>(LOGGER, inputFile, outputFile, APPROOT) {};

(note the {} at the end)

That would allow access to the generic type arguments of the superclass - this time the superclass is the ServiceTransformer class itself.

However, note that every time you use this anonymous class construct, a new class is created by the compiler which may affect the performance of your application.

If you are often using a fixed set of <In, Out> types, create a proper top-level named subclass for it and reuse that, as suggested in other answers. Eg.

class StringIntTransformer extends ServiceTransformer<String, Integer>

if you use the combination often.

The main problem is that generic type arguments can be retrieved only from an inheritance context, you can see it clearly from the method name:

getGenericSuperclass()

This means that you need to instantiate a subtype of ServiceTransformer<I,O> to be able to query for the type arguments.

This can be done in two ways, you can have a custom real type as

public static class MyServiceTransformer extends ServiceTransformer<ReactivatePrepaidSubscriberInput,ReactivatePrepaidSubcriberOutput>
{
  MyServiceTransformer()
  {
    super(...);
  }
}

Or you can define it anonymously:

new ServiceTransformer<ReactivatePrepaidSubscriberInput, ReactivatePrepaidSubcriberOutput>(....){}

First you need a deeper inheritance.

public abstract class AbstractServiceTransformer<SERVICE_I, SERVICE_O> extends Loggable {

private Class<SERVICE_I> serviceInputClass;
private Class<SERVICE_O> serviceOutputClass;

public ServiceTransformer(Logger logger, String inputXslt, String outputXslt, String appRoot) {
        serviceInputClass = TypeUtils.getParametrizedType(this.getClass(),0)
        serviceOutputClass = TypeUtils.getParametrizedType(this.getClass(),1)
}

...
}

Then here your actual implementation.

public class ServiceTransformer extends AbstractServiceTransformer<Service1, Service2> {
    ...
}

After that you can read class of type parameters like this.

public final class TypeUtils {

    /**
     * Returns parameterized type as a Class.
     */
    public static <T> Class<T> getParametrizedType(Class<?> baseClass, int index) {
        final Type genericSuperClass = baseClass.getGenericSuperclass();

        if (!(genericSuperClass instanceof ParameterizedType)) {
            throw new IllegalArgumentException("Your super class need to have a parameterized type");
        }

        final ParameterizedType parameterizedType = (ParameterizedType) genericSuperClass;
        final Type[] types = parameterizedType.getActualTypeArguments();

        if (types[index] instanceof Class<?>) {
            return (Class<T>) types[index];
        } else {
            throw new IllegalArgumentException("Parameter type is mismatched");
        }

    }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM