如何推广静态clousure?

Pro*_*orX 5 java generics java-8

我有以下场景:两个验证助手

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)}
Run Code Online (Sandbox Code Playgroud)

...和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)}
Run Code Online (Sandbox Code Playgroud)

方法from是一个静态工厂方法,它接收一个谓词和一条消息,最终验证失败.

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
}
Run Code Online (Sandbox Code Playgroud)

借助验证界面,您可以享受非常流畅的界面

    @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
    }
Run Code Online (Sandbox Code Playgroud)

所以我可以使用闭包notNull开始验证.

示例:使用NumberValidationHelper

public class MyValidate {
    void validate(int toValidate) {
        notNull.and(lowerThan(100)).test(toValidate).isValid();
    }
}
Run Code Online (Sandbox Code Playgroud)

我基于这篇文章开发了这个验证框架.

好吧,notNull包含一个独立于类型的行为,所以我想删除这两个帮助器的重复.在没有失去流体界面的情况下,我找不到明显的形状.

因为变量是静态的,所以不能使用泛型并扩展行为.

public abstract class GenericHelper<K> {
    public static Validation<K> notNull = SimpleValidation.from(o -> o != null, "must not be null.");
}
Run Code Online (Sandbox Code Playgroud)

它也不打扰我输入Validation with Object,如下所示:

public abstract class GenericHelper {

    public static Validation<Object> notNull = SimpleValidation.from(o -> o != null, "must not be null.");
}
Run Code Online (Sandbox Code Playgroud)

...因为在调用链中,它会给出编译错误,因为notNull的结果将是Validation <Object>并且将期望验证<Integer>

notNull.and(lowerThan(100)).test(toValidate).isValid(); //Does not compile
Run Code Online (Sandbox Code Playgroud)

是否有任何方法可以使用Java 8功能,使这个界面保持流畅,远离我上面尝试过的解决方案?

感谢

Hol*_*ger 5

您应该放宽通用签名and,允许Validation<T>使用更具体的T参数Validation<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);
    };
}
Run Code Online (Sandbox Code Playgroud)

坚持你的例子,你仍然无法写

void validate(int toValidate) {
    notNull.and(moreThan(100)).test(toValidate).isValid();
}
Run Code Online (Sandbox Code Playgroud)

作为moreThan返回Validation<String>哪些不可以testint,但斑点这样的错误是泛型是怎么一回事(我想,你有另一个moreThan在您的实际代码库方法,你没有在你的问题包括).但是以下内容现在可以用于您的示例:

void validate(int toValidate) {
    notNull.and(lowerThan(100)).test(toValidate).isValid();
}
Run Code Online (Sandbox Code Playgroud)

有时,您需要在更通用的验证之前测试更具体类型的验证,这仍然不适用于上面显示的方法.一种解决方案是使用与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);
    };
}
Run Code Online (Sandbox Code Playgroud)

或者您创建一个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);
    };
}
Run Code Online (Sandbox Code Playgroud)

请注意,该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);
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

所以你仍然可以写

notNull.and(lowerThan(100)).test(toValidate).isValid();
Run Code Online (Sandbox Code Playgroud)

但是在达到限制时,例如

Validation<Object> anotherCriteria;
…
lowerThan(100).and(anotherCriteria).test(toValidate).isValid();
Run Code Online (Sandbox Code Playgroud)

不起作用,你可以诉诸

Validation.and(lowerThan(100), anotherCriteria).test(toValidate).isValid();
Run Code Online (Sandbox Code Playgroud)