【问题标题】:Generic parameter type that accepts void接受 void 的通用参数类型
【发布时间】:2012-08-21 14:39:11
【问题描述】:

我正在尝试为我正在从事的开源项目创建 API,并且在尝试扩展 API 同时保持语义与当前 API 一致时遇到了障碍。我希望能够定义一个带有通用参数的方法签名,该参数接受调用 any 方法签名的结果。 “any”意味着包括void 方法。我已经知道你不能直接定义void 的参数类型——请不要重复这个明显的事实。不明显的是,是否有任何技巧可以将 void 方法调用作为参数提供给方法(即忽略)。

背景故事,所以这更能说明我为什么要做这样的事情,以及我的设计目标和约束是什么,以防上述是不可能的(我担心它是):

我当前的 API 定义了一种非常可重复的方法模式,如下所示:

public <T,V> Function<T,V> functionFor(V ignoredRetVal) {...}
public <T>   Predicate<T>  predicateFor(V ignoredRetVal) {...}
public <T>   Filter<T>     filterFor(V ignoredRetVal) {...}

顾名思义,参数被忽略,甚至没有在实现中使用。在使用中,ignoredRetVal 被替换为对动态代理的方法调用。由于在调用方法之前会评估参数,因此在外部函数(functionForpredicateFor 等)之前调用此动态代理方法。动态代理调用记录调用的Method(或方法链),并将其转换为Function对象(Guava)或来自多个函数库的其他类似函数的对象。

我现在要做的是创建一个类似的语义来捕获仅用于副作用而不需要返回类型的方法调用(例如Functional Java'sEffect。如果非空返回提供了类型,它被忽略。如果提供了 void 返回类型,它也被忽略和接受。关键是语义必须以某种方式强制代理方法在另一个提取截获的代理方法调用的方法之前被调用。并且由于我们只对副作用感兴趣,候选方法可能包括void 方法。理想情况下,它看起来像:

public <T, V> Effect<T> effectFor(V ignoredRetVal) {...}

(它已经适用于非 void 返回类型),它可以按如下方式使用:

Effect<MyClass> effect1 = effectFor (proxyOfMyClass.nonVoidMethod());// OK :-)
Effect<MyClass> effect2 = effectFor (proxyOfMyClass.orVoidMethod()); // Problem!!

正如我所说,恐怕我正在寻找的语义不能直接支持。如果不是,那么任何替代方案都应该在精神上接近我建立的模式。此外,我的 API 的全部目标是减少内部类实现的“垂直噪音”,我不喜欢双大括号初始化器。无论提供什么建议,我都在寻找一种支持简洁的语义,尤其是单语句语义。

【问题讨论】:

  • 不幸的是,我认为这是您通过 Mockito 等最好的库看到的基本限制。 void 根本不是表达式,也不可能。
  • 是的,Mockito 实际上是我的灵感来源,我担心这是同一个问题导致他们同时保留 when().thenX()doX().when()。但我仍在寻找最适合我需要的替代品。我认为我什至不能使用类似 Mockito 的方式来反转调用,因为最后我必须返回一个新的 Effect 作为最终结果。所以我必须在我的语句结束时在一个返回Effect的方法调用周围加上一个结束参数。

标签: java parameters void return-type


【解决方案1】:

我认为您永远无法将void 强制转换为表达式,特别是如果您不喜欢双括号破解。

您可以在 API 设计中遵循 Mockito 的示例。通常,您会像这样设置一个模拟:

when(mockedInstance.someMethod()).thenThrow(new IllegalArgumentException());

但是对于空,你可以这样做:

doThrow(new IllegalArgumentException()).when(mockedInstance).someMethod();

同样,您可以枚举Effect&lt;T&gt; 的方法,使它们成为您库的静态方法。

例如如果Effect&lt;T&gt;doSomething(),那么你会反转它,比如

doSomething().onEffectFor(proxyInstanceOfA).methodA();

但这假设Effect&lt;T&gt;的相关方法本身不返回值。

如果这不是一个选项,并且您需要 Effect&lt;T&gt;,您可以将其设为有状态,如下所示:

VoidEffect<MyType> effect = effectForVoid(proxyOfMyClass);
effect.on().myVoidMethod();

其中VoidEffect&lt;T&gt; 实现Effect&lt;Void&gt;on() 返回传入的代理(或不同的代理)。如果在与effect 交互之前没有调用on(),那么您可能会想要抛出IllegalStateException

【讨论】:

  • 正如我刚刚交叉发布到您的评论,最终结果必须是产生一个Effect(第 3 方库,函数式 Java),所以我不能让最后一件事执行代理方法。整个语句的结尾必须导致创建并返回一个新的Effect
  • @Kevin:很抱歉在没有警告的情况下将评论变成了答案。我添加了另一个选项,但它可能是您想到的。我想不出一个单行,在一行中缺少两个语句。
  • 我已经考虑了很长时间。我不确定我是否会真正实现它,但我认为您的VoidEffect 解决方案是我可能想要的最接近的解决方案。所以迟到的谢谢。
猜你喜欢
  • 1970-01-01
  • 2016-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-02
  • 1970-01-01
相关资源
最近更新 更多