【问题标题】:Generic "is null" predicate [duplicate]通用“为空”谓词[重复]
【发布时间】:2018-08-22 18:35:14
【问题描述】:

我写了这个通用谓词:

private static <T> Predicate<T> isNull(){
    return Objects::isNull;
}

但我不能将它与像这样的其他谓词结合使用:

private static Predicate<String> isEmpty(){
    return string -> string.isEmpty();
}

因为这个sn-p不会编译(期望Predicate在or操作):

isNull().or(isEmpty())

有解决办法吗?谢谢!

【问题讨论】:

  • 这应该可以 - &lt;YourClassName&gt;.&lt;String&gt;isNull().or(isEmpty());
  • @JBNizet 恕我直言,问题已完成,isNull().or(isEmpty()) 的使用无法与 OP 共享的实用程序一起编译。

标签: java generics java-8 predicate


【解决方案1】:

由于isNull()是泛型的,这样组合时编译器无法推断出泛型参数,所以需要显式指定类型参数。

为此,您必须使用类名进行限定,例如Test:

Test.<String>isNull().or(isEmpty())

完整示例:

public class Test {
    public static void main(String[] args) {
        Predicate<String> isNullOrEmpty = Test.<String>isNull().or(isEmpty());
        System.out.println(isNullOrEmpty.test(null));   // true
        System.out.println(isNullOrEmpty.test(""));     // true
        System.out.println(isNullOrEmpty.test("Foo"));  // false
    }
    private static <T> Predicate<T> isNull(){
        return Objects::isNull;
    }
    private static Predicate<String> isEmpty(){
        return string -> string.isEmpty();
    }
}

您也可以通过将每个部分分配给一个变量来解决它:

Predicate<String> isNull = isNull(); // String is inferred from assignment operator
Predicate<String> isEmpty = isEmpty();
Predicate<String> isNullOrEmpty = isNull.or(isEmpty);

或者只是第一部分:

Predicate<String> isNull = isNull();
Predicate<String> isNullOrEmpty = isNull.or(isEmpty());

【讨论】:

    【解决方案2】:

    在我们的项目中,我们有一个小解决方法:

    private static <T> Predicate<T> allOr(Predicate<T> ... predicates) {
        return Arrays.stream(predicates).reduce(Predicate::or).orElse(x -> true);
    }
    

    并与它一起使用:

    Predicate<String> both = allOr(isEmpty(), isNull());
    

    【讨论】:

    • 不错。它将自动选择所有谓词共有的T。 --- 不过,我会添加if (predicates.length == 0) throw new IllegalArgumentException();,而不是默认为true 值,特别是因为我预计默认值为false,即在谓词列表中模拟一个虚拟() -&gt; false
    • @Andreas 哦,我们在内部审查中也对此进行了辩论...... :) 我同意你的看法,但显然我团队中的所有人都没有这样做
    • @Andreas 好吧,() -&gt; falseidentity element 代表 Predicate::or 就像 () -&gt; true 代表 Predicate::and,所以它确实更适合作为空谓词列表的默认值。或者,您可以使用 allOr(Predicate&lt;T&gt; first, Predicate&lt;T&gt;... more) 强制执行至少一个参数
    • @Holger 这确实是正确的,我将为我们的代码打开一个内部问题:(以及为什么我没有指定Predicate first, Predicate ... allOthers...谢谢!!
    【解决方案3】:

    当您调用isNull() 时,没有上下文可以推断T 的实际类型是什么。

    isNull().or(..) 此处需要 Predicate&lt;? super Object&gt;,因此您不能将 Predicate&lt;String&gt; 传递给它。

    翻转它们会起作用 (isEmpty().or(isNull())) isEmpty() 现在返回一个 Predicate&lt;String&gt; 并且 isEmpty.or(..) 需要一个 Predicate&lt;? super String&gt;,所以你可以传递一个 Predicate&lt;Object&gt;

    但这不是你想要的,因为你想在检查是否为空之前进行空检查(感谢 Slaw@ 指出这一点。专注于让它编译我没有注意它的含义/语义)。

    你可以通过写作告诉编译器T的类型是什么

    <YourClassName>.<String>isNull().or(isEmpty());
    

    【讨论】:

    • 翻转它们不等于调用str.isEmpty() || str == null吗?如果strnull,第一部分将抛出NullPointerException
    • @Slaw 哦,是的。你是对的..
    • @Slaw 我已经更新了答案(说它在这种情况下不起作用)。但我决定保持原样 - 看看翻转是如何工作的(撇开每个谓词所做的当前上下文)可能很有用
    【解决方案4】:

    专门针对String,您可能只是在寻找一个像这样的Predicate -

    private static Predicate<String> isNullOrEmpty() {
        return s -> s == null || s.isEmpty();
    }
    

    这可以进一步用作

    List<String> strings = Arrays.asList("", "one", "two", null, "three");
    strings.stream().filter(isNullOrEmpty()).forEach(System.out::println);
    // ofcourse the above would print "null" and is less useful
    

    或者借助 Java11 中 Predicate.not 的能力,更直观且看似有用的方法是 -

    List<String> strings = Arrays.asList("", "one", "two", null, "three");
    strings.stream().filter(Predicate.not(isNullOrEmpty)).forEach(System.out::println); 
    // this would print one, two and three
    

    【讨论】:

      猜你喜欢
      • 2015-01-21
      • 2016-12-20
      • 2020-07-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-12
      • 1970-01-01
      相关资源
      最近更新 更多