我有一个漂亮干净的界面,如下所示:
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())
都无法编译。
或者有更好的方法来实现这个目标吗?
Class<I>
是你的问题。
您想要抽象类型本身的概念 – 您不需要“DoublerInput 的某个实例”这个概念,不,您想要抽象“DoublerInput”的概念 – 整个类。
这样想是有道理的:“啊,好吧,DoublerInput.class
,属于
Class<DoublerInput>
类型,将充当那个东西!”这是一个明智的想法,但是,这是错误的。
您正在寻找的答案是
工厂。
是的,陈词滥调,但别再讲笑话了。这确实是它的用途。Class
由于多种原因不起作用:
class DoublerInput
。根本没有办法规定任何“实现
Input
的类”必须有一个无参数构造函数。您也不能定义任何类级别的方法;毕竟,不存在静态成员接口之类的东西。
j.l.Class
无法表示类型参数类型。你不能做
List<String>.class
,虽然你可以写
Class<List<String>> x;
,但这毫无意义;没有类引用可以唯一地具体表示“字符串列表”的概念。只有
List.class
。不是
List<String>.class
。
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 做到了;科特林做到了。涉及的样板数量很少。
建造工厂。