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