通过反射创建扩展类

问题描述 投票:0回答:1

我有一个漂亮干净的界面,如下所示:

public interface Mapping<I extends Input, O extends Output> {
    O calculate(I input);

    Class<I> getInputClass();

    Class<O> getOutputClass();
}

为了简单起见,我们假设

Input
Output
是空接口。

我想在运行时定义映射并通过反射实例化它们。此类

Mapping
的一个示例如下:

public class DoublerMapping implements Mapping<DoublerInput, DoublerOutput> {
    @Override
    DoublerOutput calculate(DoublerInput input){
       DoublerOutput output = new DoublerOutput();
       output.setValue(input.getValue() * 2);
       return output;
    }

    @Override
    Class<DoublerInput> getInputClass(){
       return DoublerInput.class;
    }

    @Override
    Class<O> getOutputClass(){
       return DoublerInput.class;
    }
}

但是,当通过反射实例化

Mapping
时,我只能使用
getInputClass()
getOutputClass()
分别实例化
Inputs
Outputs
(利用
mapping.getInputClass().getDeclaredConstructor().newInstance()
),而不是它们的实际扩展。我不希望
Mapping
采用通用
Input
,因为我想确保它获得它所期望的正确类
I
。我也无法将
getValue()
setValue()
方法拉到接口,因为它们可以接受任何参数并具有不同的签名。

如何在运行时创建

I extends Input
并将其反馈给参数化
Mapping
类?甚至
mapping.execute(mapping.getInputClass().getDeclaredConstructor().newInstance())
都无法编译。

或者有更好的方法来实现这个目标吗?

java reflection
1个回答
0
投票

Class<I>
是你的问题。

您想要抽象类型本身的概念 – 您不需要“DoublerInput 的某个实例”这个概念,不,您想要抽象“DoublerInput”的概念 – 整个类。

这样想是有道理的:“啊,好吧,

DoublerInput.class

,属于 
Class<DoublerInput>
 类型,将充当那个东西!”

这是一个明智的想法,但是,这是错误的。

您正在寻找的答案是

工厂

是的,陈词滥调,但别再讲笑话了。这确实是它的用途。

Class

 由于多种原因不起作用:

  1. 您无法在其中定义任何内容。例如,当您编写反射代码时,您只是假设有一个无参数的公共构造函数。如果它不存在,无论是在您的框架中,还是在任何编写者中,您都不会收到编译时警告

    class DoublerInput

    。根本
    没有办法规定任何“实现Input
    的类”必须有一个无参数构造函数。您也不能定义任何类级别的方法;毕竟,不存在静态成员接口之类的东西。

  2. j.l.Class

     无法表示类型参数类型。你不能做
    List<String>.class
    ,虽然你可以
    Class<List<String>> x;
    ,但这毫无意义;没有类引用可以唯一地具体表示“字符串列表”的概念。只有
    List.class
    。不是
    List<String>.class

  3. 也许是插曲,但是,

    int.class

    存在,但是,泛型不能做基元。从这个意义上说,
    Class<T>
    是双重的坏事;如果 
    T
     包含泛型,那么 
    j.l.Class
     不能代表它们,而 
    j.l.Class
     可以代表泛型不能代表的东西。

工厂可以让你修复

所有问题。您定义工厂的概念,然后添加您需要的任何内容。例如:

abstract class InputFactory<I extends Input> { abstract I create(); }
现在你可以做:

final class DoublerInputFactory extends InputFactory<DoublerInput> { @Override DoublerInput create() { return new DoublerInput(); } }
现在一切都很好:您不再接受 

Class<I>

,而是接受 
InputFactory<I>
,并且您可以在该实例上调用 
.create()
 来替换 
.getConstructor().newInstance()
,这避免了一大堆难以处理的奇怪异常。在您尝试执行的操作的上下文中进行解释,并且您
保证为您提供的对象提供支持的代码实现了这一点;如果没有,它一开始就不会编译。

您现在还可以添加您需要的任何内容。想要一个概念(例如

DoublerInput

)能够自我命名吗?例如,不是 
DoublerInput 的某个特定 实例
,而是 DoublerInput 作为一个整体的概念?

只需在工厂中塞入一个

String getName()

方法即可,简单!

想要这个概念能够克隆一个实例吗?将其添加到工厂。

想要传达这样的概念:任何实现类

必须有一个构造函数,比如说,一个int

?不要这样做,而是将 
abstract I create(int arg);
 粘贴到你的工厂类中。

这解决了许多问题,“当我尝试使用反射作为重新发明工厂的蹩脚方式时,泛型变得很糟糕”是这将解决的众多问题之一。

当基于 JVM 的语言发明类型级接口的概念时,它们是通过将工厂模式作为一个整体纳入语言中来实现的。 Scala 做到了;科特林做到了。涉及的样板数量很少。

建造工厂。

© www.soinside.com 2019 - 2024. All rights reserved.