背景
Java接口指定调用者和被调用者开发方之间的契约。例如:
public interface SomeContainer<T> {
public boolean add(T value) throws SomeException;
}
上面的方法必须返回一个布尔值或抛出给定的异常,而不是其他任何东西。
问题说明
如何为基于Play Promise的方法执行相同的操作?承诺没有&#34;扔&#34;例外;相反,promises执行Promise.failure(ExceptionHere)。因此,我的界面并没有&#34;抛出&#34;任何东西:
public interface SomeContainer<T> {
public Promise<boolean> add(T value);
}
但是,上面的定义并不能阻止实现类执行Promise.failure() - 这不是一个非常好的接口定义。
一种可能性是返回&#34;结果&#34;可以包含布尔值或多个允许的异常之一的对象。但是,这似乎是hackish,并且仍然无法阻止实现代码调用Promise.failure()。
问题
如何编写一个有效的基于Play Promise的界面,其中指定了所有返回值和所有可接受的异常,并且不允许其他任何内容?
答案 0 :(得分:3)
您所描述的是异常的基本限制 - 它们的规范仅在直接从方法中抛出时才有效。问题不是特定于承诺,它会在任何时候将某些内容执行打包到通用接口中,并且在使用lambdas时尤为相关。考虑做一个List.forEach
,它不会抛出异常,但通常会将lambda传递给它(它将实现java.util.function.Function),如果lambda引发异常怎么办?答案是,它不能,除非它是一个未经检查的例外,然后无法静态地知道该异常将被抛给forEach
的调用者。
唯一真正的答案是不要使用例外。如果你与函数编程纯粹主义者交谈,他们会说永远不会使用异常,而不是异常,你应该使用不相交的类型。 Play的Java API实际上提供了这样一种类型,它是play.libs.F.Either
。惯例是左边应该是错误,右边是值。也就是说,Play的任何一种类型都非常有限,而且与自身或其他东西的搭配并不是很好。 Functional Java库提供的任一类型都更加完整,并且编写得很好。如果你认为对类型安全尽可能严格,特别是有例外,那么这可能是你的库,即使不使用promises,将返回类型中的编码错误提供比异常更好的类型安全性,并使其非常直接撰写和重用错误处理。如果这听起来对你来说是个不错的选择,那么你也应该考虑使用比Java更强的输入语言,例如Scala。
如果你像我一样对事情更加务实,那么你将采用混合方法。在某些情况下,调用者明确处理错误非常重要,在这种情况下,应该在类型中对它们进行编码,这样做的一个例子是表单验证,你不想只是想抛出一个错误,你想通过渲染一个包含多个有意义的错误消息的页面来处理它 - 这是强类型错误处理真正发光的一个完美的例子,你可以返回一个错误列表,每个字段有一个问题表单,但您不能抛出异常列表。在其他情况下,错误确实是特殊的事情,通常没有特定的处理策略,相反,它们应留在堆栈中的一般性捕获中以便一般地处理。例如,如果数据库已关闭,那么您通常无法从中恢复。在这些情况下,通过抛出未经检查的异常并让通用错误处理完成它,您将失去任何东西。