【问题标题】:How to generalize a static clousure?如何概括静态闭包?
【发布时间】:2018-04-28 02:28:15
【问题描述】:

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

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();
    }
}

这个验证框架是我根据this文章开发的。

好吧,notNull 包含了一个与类型无关的行为,所以我想删除这两个助手的重复项。 在不丢失流体界面的情况下,我找不到明显的形状。

因为变量是静态的,例如,你不能使用泛型和扩展行为。

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

此外,使用 Object 键入验证也不会打扰我,如下所示:

public abstract class GenericHelper {

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

... 因为在调用链中,它会给出编译错误,因为 notNull 的结果将是一个 Validation 并且期待一个 Validation

notNull.and(lowerThan(100)).test(toValidate).isValid(); //Does not compile

有没有什么方法可以使用 Java 8 的函数特性来保持这个接口的通用性,而不是我上面尝试过的解决方案?

感谢

【问题讨论】:

    标签: java generics java-8


    【解决方案1】:

    您应该放宽and 的通用签名,允许Validation&lt;T&gt; 使用更具体的T 作为参数,生成Validation&lt;T&gt; 作为结果:

    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();
    }
    

    因为 moreThan 返回一个 Validation&lt;String&gt; 不能是 testint,但发现此类错误是泛型的全部意义所在(我想,您的实际代码库中有另一个 moreThan 方法你没有包括在你的问题中)。但以下内容现在适用于您的示例:

    void validate(int toValidate) {
        notNull.and(lowerThan(100)).test(toValidate).isValid();
    }
    

    有时,您需要在更通用的验证之前测试更具体类型的验证,但上面显示的方法仍然无法正常工作。一种解决方案是走与 JDK 开发人员相同的路线,并用 Function.compose(before) 增加 Function.andThen(after),允许交换角色

    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();
    

    【讨论】:

    • 超级干净,不错。我可以在这里问你的意见吗:stackoverflow.com/questions/50124348/…
    • 这段代码 sn-p 有错别字和精神失误:notNull.and(moreThan(100)).test(toValidate).isValid(); 我的意图是参考我在问题中省略的方法,如下所述:public static &lt;N extends Number &amp; Comparable&lt;N&gt;&gt; Validation&lt;N&gt; greaterThan(N min) 所以你的回答是我参考下面智能地解决了这个问题。 default &lt;T extends K&gt; Validation&lt;T&gt; and(Validation&lt;T&gt; other) { return param -&gt; { ValidationResult firstResult = this.test(param); return ! firstResult.isValid()? firstResult: other.test(param); }; }
    猜你喜欢
    • 2011-11-11
    • 2017-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-03
    • 1970-01-01
    相关资源
    最近更新 更多