【问题标题】:How to create a new instance from within abstract class in an instance method?如何在实例方法中从抽象类中创建新实例?
【发布时间】:2021-11-22 17:34:15
【问题描述】:

我希望能够在类型为抽象的变量中创建一个类的新实例。

class Scratch {
    public static void main(String[] args) {
        Implementation implementation = new Implementation();
        // ...Later in the code Implementation is abstracted away and it's AbstractClass
        AbstractClass implementedAbstractClass = implementation;
        AbstractClass newInstance = implementedAbstractClass.newInstance();
    }
}

abstract class AbstractClass {
    protected abstract AbstractClass createNewInstance();
    
    public AbstractClass newInstance() {
        return createNewInstance();
    }
}

class Implementation extends AbstractClass {
    @Override
    protected AbstractClass createNewInstance() {
        return new Implementation();
    }
}

这可行,但我想避免在我所做的每个实现中都必须实现 createNewInstance() 的样板。

我尝试更改 AbstractClass 以接受类型参数:AbstractClass<T extends AbstractClass<T>>,然后是 Implementation extends AbstractClass<Implementation> 然后调用new T(),当然这也不起作用,因为Java在编译时不知道T是什么,所以它不能在类文件中添加构造指令。

之后我尝试调用AbstractClass.class.getDeclaredConstructor().newInstance(),但这更没有意义,因为我只是在尝试构造一个 AbstractClass 的实例,这是不可能的,因为它是抽象的,也不是我需要的。

【问题讨论】:

  • stackoverflow.com/questions/56385734/… 相关但不重复,因为该问题希望从静态上下文中实例化。
  • 您可以将构造函数作为方法引用传递:super(Implementation::new)。但是没有办法在抽象中安全地引用构造函数,因为构造函数不是继承的。
  • @shmosel 效果很好,我学到了一些关于函数式接口的新知识。如果我将您的评论转化为答案,您介意吗?
  • 我实际上开始写它作为答案,但将其更改为评论,因为感觉有点不满意。不过我会发给你的。
  • 我花了几个步骤和一些研究才能在我的示例代码中实现它,这就是为什么如果它是一个完整的答案并显示实现它会很有用。我相信这将是一个很好的答案! :)

标签: java abstract-class instantiation


【解决方案1】:

AbstractClass.class.newInstance() 的尝试非常接近。

要构造实现类的实例,我们需要使用this 关键字获取它自己的类。由于抽象类无法实例化,所以在抽象类的实例方法中使用this会引用实现类。

正确的调用代码是this.getClass().getDeclaredConstructor().newInstance()

那么createNewInstance()方法就可以去掉了:

class Scratch {
    public static void main(String[] args) {
        Implementation implementation = new Implementation();
        // ...Later in the code Implementation is abstracted away and it's AbstractClass
        AbstractClass implementedAbstractClass = implementation;
        AbstractClass newInstance = implementedAbstractClass.newInstance();
    }
}

abstract class AbstractClass {
    public AbstractClass newInstance() {
        try {
            return this.getClass().getDeclaredConstructor().newInstance();
        } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException("Failed to construct instance of " + this, e);
        }
    }
}

class Implementation extends AbstractClass {
}

【讨论】:

  • 虽然在构造函数不可访问的情况下会导致运行时异常,但我更喜欢这种方式,因为它不会向实现类引入责任,如果在 API 中使用这可能根本没有意义。如果异常很重要,可以进行启动检查以确保所有实现者都有可访问的构造函数等。或者只是使用反射将构造函数设置为可访问。
【解决方案2】:

构造函数不是继承的,因此没有安全的方法在抽象中引用它们。但是您可以将它们作为方法引用传递:

abstract class AbstractClass {
    private final Supplier<AbstractClass> constructor;

    protected AbstractClass(Supplier<AbstractClass> constructor) {
        this.constructor = constructor;
    }

    public AbstractClass newInstance() {
        return constructor.get();
    }
}

class Implementation extends AbstractClass {
    public Implementation() {
        super(Implementation::new);
    }
}

就个人而言,我觉得在每个实例上保留一个额外的引用来存储本质上是静态的信息有点问题。但它可以使子类更简洁(如果你仍然声明了一个构造函数)。

您还可以进一步扩展它并使超类通用(如您在问题中建议的那样),以便Implementation.newInstance() 返回Implementation 而不是AbstractClass

abstract class AbstractClass<T extends AbstractClass<T>> {
    private final Supplier<T> constructor;

    protected AbstractClass(Supplier<T> constructor) {
        this.constructor = constructor;
    }

    public T newInstance() {
        return constructor.get();
    }
}

class Implementation extends AbstractClass<Implementation> {
    public Implementation() {
        super(Implementation::new);
    }
}

【讨论】:

  • 这正是我为您提供评论的代码!这是一个很好的解决方案,因为它是类型安全的。不会有像构造函数不存在的反射那样的陷阱。
猜你喜欢
  • 1970-01-01
  • 2015-07-21
  • 1970-01-01
  • 2019-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多