【问题标题】:Instantiate generic type with constructor arguments?使用构造函数参数实例化泛型类型?
【发布时间】:2017-10-27 23:35:16
【问题描述】:

我现在正面临一个非常令人困惑的问题。

我正在开发程序库的扩展。在该库中,有多个类A、B、C和D,它们都继承自同一个超类Base

现在,我正在为每个类进行单独的扩展,A1、B1、C1 和 D1,它们都继承自 A、B、C 和 D分别。除此之外,它们都实现了一个自定义接口I1,我自己也添加了。

基类(以及所有子类,包括我的)的构造函数接受一个 E 类型的参数。

为了以简单的方式实例化这些类,我正在尝试编写一个处理程序类,以便在需要时创建一个实例。它应该像这样工作:

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

现在,当然它不会那样工作,但我确信我明白了这个想法。

到目前为止,我的扩展程序有一个工作版本,虽然非常草率,因为我不是处理程序类,而是手动检查每种类型并实例化正确的对象。由于将来我将不得不修改更多类,因此我想不惜一切代价避免这种情况,以避免文件混乱。

我知道 ctype.newInstance() 方法,但是由于 Base 类在其构造函数中需要一个参数,恐怕它不会工作。 (除非我遗漏了什么,在这种情况下请指点我!)

感谢您的帮助!

【问题讨论】:

    标签: java inheritance types


    【解决方案1】:

    好吧,您可以使用例如getConstructor(Class...) 来获取一个不同于默认无参数的构造函数。

    你会有这样的东西:

    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);
            }
        }
    }
    

    但是,我并不完全相信您需要使用反射。您现在可以使用 lambda 表达式和方法引用来做这种事情,而且通常会好很多。

    您将传递某种Function&lt;E, Base&gt;,而不是传递一个类:

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

    然后您使用方法引用创建新的处理程序:

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

    反射很好,也很强大,但它并不总是处理这类事情的最佳方式。

    【讨论】:

    • 这正是我想要的,谢谢!我希望代替我的其他人会发现这篇文章和我一样有用。
    • 我确实觉得它很有用。
    【解决方案2】:

    要调用带参数的构造函数,请使用getConstructor(Class&lt;?&gt;...) 获取ConstructorConstructor 有一个接受参数的 newInstance() 方法。

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

    异常处理留给读者练习。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多