java - 如何概括静态闭包?
问题描述
我有以下场景:两个验证助手
StringValidationHelper ...
public class StringValidationHelper {
public static Validation<String> notNull =
SimpleValidation.from(s -> s != null, "must not be null.");
public static Validation<String> moreThan(int size) {
return SimpleValidation.from(
s -> s.length() >= size,
String.format ("must have more than %s chars.", size));
}
... // More methods (lessThan, etc)}
...和 NumberValidationHelper。
public class NumberValidationHelper {
public static Validation<Number> notNull =
SimpleValidation.from(n -> n != null, "must not be null");
public static <N extends Number & Comparable<N>> Validation<N> lowerThan(N max){
return SimpleValidation.from(
n -> n.compareTo(max) == -1,
String.format("must be lower than %s.", max));
}
... // More methods like (greaterThan, etc)}
from方法是一个静态工厂方法,它接收一个 Predicate 和一条最终验证失败的消息。
public class SimpleValidation<K> implements Validation<K>{
private Predicate<K> predicate;
private String onErrorMessage;
private SimpleValidation(Predicate<K> predicate, String onErrorMessage) {
this.predicate = predicate;
this.onErrorMessage = onErrorMessage;
}
public static <K> SimpleValidation<K> from(Predicate<K> predicate, String onErrorMessage){
return new SimpleValidation<>(predicate, onErrorMessage);
}
... // Omitted for simplicity
}
感谢验证界面,您可以享受一个非常流畅的界面
@FunctionalInterface
public interface Validation<K> {
... // Omitted for simplicity
default Validation<K> and(Validation<K> other) {
return param -> {
ValidationResult firstResult = this.test (param);
return ! firstResult.isValid()? firstResult: other.test(param);
};
}
... // Omitted for simplicity
}
例如,我可以使用闭包 notNull 开始验证。
示例:使用 NumberValidationHelper
public class MyValidate {
void validate(int toValidate) {
notNull.and(lowerThan(100)).test(toValidate).isValid();
}
}
我根据这篇文章开发的这个验证框架。
好吧,notNull 包含了一个与类型无关的行为,所以我想删除这两个助手的重复。在不丢失流体界面的情况下,我找不到明显的形状。
因为变量是静态的,例如,您不能使用泛型和扩展行为。
public abstract class GenericHelper<K> {
public static Validation<K> notNull = SimpleValidation.from(o -> o != null, "must not be null.");
}
此外,使用 Object 键入 Validation 也不会打扰我,如下所示:
public abstract class GenericHelper {
public static Validation<Object> notNull = SimpleValidation.from(o -> o != null, "must not be null.");
}
...因为在调用链中,它会给出编译错误,因为 notNull 的结果将是一个 Validation< Object > 并且将期待一个 Validation< Integer >
notNull.and(lowerThan(100)).test(toValidate).isValid(); //Does not compile
有没有什么方法可以使用 Java 8 的功能特性来保持这个接口的通用性,远离我上面尝试过的解决方案?
感恩的
解决方案
您应该放宽 的通用签名and
,允许 aValidation<T>
具有更具体T
的作为参数,以产生 aValidation<T>
作为结果:
default <T extends K> Validation<T> and(Validation<T> other) {
return param -> {
ValidationResult firstResult = this.test(param);
return ! firstResult.isValid()? firstResult: other.test(param);
};
}
坚持你的例子,你仍然不能写
void validate(int toValidate) {
notNull.and(moreThan(100)).test(toValidate).isValid();
}
asmoreThan
返回 a Validation<String>
which can not test
an int
,但是发现此类错误是泛型的全部意义所在(我想,您moreThan
的实际代码库中有另一种方法,您的问题中没有包含该方法)。但是以下内容现在将适用于您的示例:
void validate(int toValidate) {
notNull.and(lowerThan(100)).test(toValidate).isValid();
}
有时,您需要在更通用的验证之前测试更具体类型的验证,这仍然不适用于上面显示的方法。一种解决方案是走与 JDK 开发人员相同的路线并增加Function.andThen(after)
a Function.compose(before)
,允许交换角色
default <T extends K> Validation<T> compose(Validation<T> other) {
return param -> {
ValidationResult firstResult = other.test(param);
return ! firstResult.isValid()? firstResult: this.test(param);
};
}
或者您创建一个static
方法,该方法允许两个参数具有比结果更广泛的类型Validation
:
static <T> Validation<T> and(Validation<? super T> first, Validation<? super T> second) {
return param -> {
ValidationResult firstResult = first.test(param);
return ! firstResult.isValid()? firstResult: second.test(param);
};
}
请注意,该static
方法可以与方便的实例方法结合使用,这样调用者只需static
在遇到泛型签名的限制时求助于该方法:
@FunctionalInterface
public interface Validation<K> {
ValidationResult test(K item);
default <T extends K> Validation<T> and(Validation<T> other) {
return and(this, other);
}
static <T> Validation<T> and(Validation<? super T> first,Validation<? super T> second){
return param -> {
ValidationResult firstResult = first.test(param);
return ! firstResult.isValid()? firstResult: second.test(param);
};
}
}
所以你仍然可以写
notNull.and(lowerThan(100)).test(toValidate).isValid();
但是当达到限制时,例如
Validation<Object> anotherCriteria;
…
lowerThan(100).and(anotherCriteria).test(toValidate).isValid();
不起作用,你可以求助于
Validation.and(lowerThan(100), anotherCriteria).test(toValidate).isValid();
推荐阅读
- swift - 将多个带有动画的 CATextLayer 添加到 CALayer 无法正常工作
- c# - HTML 渲染器 - 从 HTML 生成 PDF,但无法从 C# 流中添加背景图像
- react-native - ReferenceError:找不到变量:createStackNavigator
- laravel - Laravel FCM 通道驱动程序在排队时不起作用
- java - 如何正确关闭 Android WebRTC 连接?
- reactjs - 启动下载 onClick 不能在任何浏览器上运行?
- import - 用户在将数据集从数据库导入 HCL OneTest Data 时,是否可以从具有多个模式的任何微服务架构中导入数据?
- python - 如何让 Python 每天在特定时间运行并让它每小时重复一次?
- stata - 在Stata中重塑数据
- php - 将 ucwords 和 strtoupper 组合在一个字符串中,以符号作为分隔符