Create generic type with constructor arguments?

I am currently facing a very confusing problem.

I am developing an extension for a program library. There are several classes A, B, C and D in the specified library, which all inherit from the same superclass base.

Now I make individual extensions for each of these classes: A1, B1, C1 and D1, which all inherit from A, B, C and D respectively. In addition to this, they all implement the I1 UI, which I also added.

The constructor of the base (and all child classes, including mine) takes one argument of type E.

To instantiate these classes in a simple manner, I am trying to write a handler class that instantiates when needed. It should work something like this:

public class Handler
{
    private Base ctype;
    public Handler(Class<T extends Base implements I1> ctype)
    {
          this.ctype = new ctype.class(E.instance);
    }
}

      

Now, of course, it won't work, but I'm sure I have an idea.

As of now, I have a working version of my extension, albeit a very messy one, because instead of a handler class, I check each type manually and instantiate the corresponding object. Since I will have to modify many more classes in the future, I would like to avoid this at all costs to avoid file clutter.

I know the ctype.newInstance () method, however, since the Base class requires a parameter in its constructor, I'm afraid it won't work. (if I'm missing something, in which case, please call me!)

Thanks for your help!

+3


source to share


2 answers


Well, you can use, for example, getConstructor(Class...)

to get a constructor other than the default no-argument.

You will have something like this:

import java.lang.reflect.*;

public class Handler<B extends Base & I1> {
    private E instanceOfE = ...; // Not sure where you get the E from.
    private Constructor<B> ctor;

    public Handler(Class<B> ctype) {
        if (Modifier.isAbstract(ctype.getModifiers())) {
            throw new IllegalArgumentException(ctype + " is abstract");
        }
        if (!Modifier.isPublic(ctype.getModifiers())) {
            throw new IllegalArgumentException(ctype + " is not public");
        }
        try {
            ctor = ctype.getConstructor(E.class);
        } catch (NoSuchMethodException x) {
            throw new IllegalArgumentException(x);
        }
        Arrays.stream(ctor.getExceptionTypes())
              .filter(x -> !RuntimeException.class.isAssignableFrom(x)
                        && !Error.class.isAssignableFrom(x))
              .findFirst()
              .ifPresent(x -> throw new IllegalArgumentException(ctor + " declares a checked exception");
    }

    private B create() {
        try {
             return ctor.newInstance(instanceOfE);
        } catch (InvocationTargetException x) {
             Throwable cause = x.getCause();
             if (cause instanceof RuntimeException)
                 throw (RuntimeException) cause;
             if (cause instanceof Error)
                 throw (Error) cause;
             // This won't happen because we checked for
             // it in the constructor.
             throw new RuntimeException(cause);
        } catch (IllegalAccessException
               | InstantiationException x) {
             // These also won't happen because we checked
             // for it in the constructor.
             throw new RuntimeException(x);
        }
    }
}

      

However, I'm not so convinced that you need to use reflection. You can use lambda expressions and method references to do this kind of thing now, and it's much better overall.

Instead of passing in a class, you should pass in some kind of Function<E, Base>

:



public class Handler<B extends Base & I1> {
    public Handler(Function<E, B> ctorFn) {
        Base b = ctorFn.apply(instanceOfE);
    }
}

      

And then you create new handlers with a method reference:

Handler<A1> h = new Handler<>(A1::new);

      

Reflection is beautiful and very powerful, but it's not always the best way these days.

+2


source


To call a constructor that takes arguments, use getConstructor(Class<?>...)

to get Constructor

. Constructor

has a method newInstance()

that takes arguments.

this.ctype = ctype.getConstructor(E.class).newInstance(E.instance);

      



Exception handling is left as an exercise for the reader.

+2


source







All Articles