Force Stream::filter method to fail compile time when Predicate<? super Object> is passed rather than Predicate<? super T>

huangapple go评论103阅读模式
英文:

Force Stream::filter method to fail compile time when Predicate<? super Object> is passed rather than Predicate<? super T>

问题

我正在尝试使用预定义的身份过滤器来与流 API 一起使用。不幸的是,我无法正确返回与流 API 文档符合的通用谓词。

根据反编译器,这是Stream::filter的定义:

public interface Stream<T> extends BaseStream<T, Stream<T>> {
    Stream<T> filter(Predicate<? super T> var1);
}

我在任何支持流的 Java 版本(8~15)中都遇到了问题。问题与我的实现无关。以下代码实际上足以重现此问题:

Collection<String> result = Stream.of("A", "B", "C")
                                  .filter(new Object()::equals)
                                  .filter(Integer.valueOf(-1)::equals)
                                  .collect(Collectors.toSet());

在这里,应用了两个谓词,它们都不符合<? super String>...

根据这个答案,这种行为似乎很奇怪...

我应该如何防止我的库用户通过随机对象平等检查等方式对ServerState进行过滤?

理想情况下,我希望始终返回正确的Predicate<? super T>,不幸的是,这没有任何编译时错误支持...

在这种情况下,使用代码检查工具并不是解决方案。

尽管我知道如何使用下界通配符,但我之前忽略了Predicate<? super Integer>实际上可以成功转换为Predicate<? super String>

其中:

Predicate<? super String> stringPredicate = (Predicate<? super String>)Filters.is_tClass(Integer.class, 4);
Predicate<? super Server> serverPredicate = (Predicate<? super Server>)Filters.is_comparable(5);

Collection<Integer> result = Stream.of(1, 2, 3)
                                   .filter((Predicate<? super Integer>)stringPredicate)
                                   .filter((Predicate<? super Integer>)serverPredicate)
                                   .filter(Filters.is(new Object()))
                                   .collect(Collectors.toSet());

导致[]空结果集。

这是我目前的情况,但对其中任何部分都不满意:

import java.util.Collection;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Main {

    public static void main(String[] args) {

        Collection<Integer> result = Stream.of(1, 2, 3)
                                           //.filter(Filters.is_tClass(Integer.class, 4)) // 强制用户提供目标类
                                           //.filter(Filters.is_comparable(5)) // 仅使用 Comparable
                                           .filter(Filters.is(new Server())) // 在运行时使用自定义异常失败
                                           .collect(Collectors.toSet());

        System.out.println(result);
    }

    private static class Server {
    }

    private static class Filters {

        private static <T> Predicate<? super T> is(T other) {

            return t -> {

                // 简单的类相等性检查 - 容易出错!
                Class<?> tClass = t.getClass();
                Class<?> otherClass = other.getClass();

                if (!tClass.equals(otherClass)) {

                    throw new RuntimeException(
                        String.format("Check equality for [%s ? %s] seems odd. Can not continue...", tClass, otherClass));
                }
                return t.equals(other);
            };
        }

        static <T> Predicate<? super T> is_tClass(Class<T> tClass, T other) {

            return is(other);
        }

        static <T extends Comparable<T>> Predicate<? super T> is_comparable(T other) {

            return is(other);
        }
    }
}

在发布示例之前不存在is_*类型的方法名称,因此将被删除...

编辑

尽管我知道如何使用下界通配符,但我之前忽略了Predicate<? super Integer>实际上可以成功转换为Predicate<? super String>

其中:

Predicate<? super String> stringPredicate = (Predicate<? super String>)Filters.is_tClass(Integer.class, 4);
Predicate<? super Server> serverPredicate = (Predicate<? super Server>)Filters.is_comparable(5);

Collection<Integer> result = Stream.of(1, 2, 3)
                                   .filter((Predicate<? super Integer>)stringPredicate)
                                   .filter((Predicate<? super Integer>)serverPredicate)
                                   .filter(Filters.is(new Object()))
                                   .collect(Collectors.toSet());

导致[]空结果集。

英文:

I'm playing around with predefined Identity filters for use with the stream api. Unfortunately I'm unable to properly return a generic predicate that is compliant with the stream api documentation.

According to the de-compiler here is the Stream::filter definition:

public interface Stream&lt;T&gt; extends BaseStream&lt;T, Stream&lt;T&gt;&gt; {
Stream&lt;T&gt; filter(Predicate&lt;? super T&gt; var1);

I'm facing the issue with any Java version that has Streams support (8~15). The issue has nothing to do with my implementation. This code actually is enough in order to reproduce it:

        Collection&lt;String&gt; result = Stream.of(&quot;A&quot;, &quot;B&quot;, &quot;C&quot;)
.filter(new Object()::equals)
.filter(Integer.valueOf(-1)::equals)
.collect(Collectors.toSet());

Here, two predicates are applied where both of them aren't &lt;? super String&gt; compliant...

According to this answer this behavior seems to be strange...

How should I prevent users of my library from filtering on ServerState by random Object equality check, etc...?

Ideally I would like to always return proper Predicate<? super T> unfortunately that is not backed up by any compile time error...

Using a linter is not a solution in that case.

Even though I know how lower bounded wildcards work what I've been missing is that a Predicate&lt;? super Integer&gt; could be successfully casted to Predicate&lt;? super String&gt;.

Where:

Predicate&lt;? super String&gt; stringPredicate = (Predicate&lt;? super String&gt;)Filters.is_tClass(Integer.class, 4);
Predicate&lt;? super Server&gt; serverPredicate = (Predicate&lt;? super Server&gt;)Filters.is_comparable(5);
Collection&lt;Integer&gt; result = Stream.of(1, 2, 3)
.filter((Predicate&lt;? super Integer&gt;)stringPredicate)
.filter((Predicate&lt;? super Integer&gt;)serverPredicate)
.filter(Filters.is(new Object()))
.collect(Collectors.toSet());

results in [] empty resultset.

Here is what I have so far, but not happy with any of it:

import java.util.Collection;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
Collection&lt;Integer&gt; result = Stream.of(1, 2, 3)
//.filter(Filters.is_tClass(Integer.class, 4)) // enforce user to provide target class
//.filter(Filters.is_comparable(5)) // use only Comparable
.filter(Filters.is(new Server())) // fail runtime with custom exception
.collect(Collectors.toSet());
System.out.println(result);
}
private static class Server {
}
private static class Filters {
private static &lt;T&gt; Predicate&lt;? super T&gt; is(T other) {
return t -&gt; {
// simple class equality check - error prone!
Class&lt;?&gt; tClass = t.getClass();
Class&lt;?&gt; otherClass = other.getClass();
if (!tClass.equals(otherClass)) {
throw new RuntimeException(
String.format(&quot;Check equality for [%s ? %s] seems odd. Can not continue...&quot;, tClass, otherClass));
}
return t.equals(other);
};
}
static &lt;T&gt; Predicate&lt;? super T&gt; is_tClass(Class&lt;T&gt; tClass, T other) {
return is(other);
}
static &lt;T extends Comparable&lt;T&gt;&gt; Predicate&lt;? super T&gt; is_comparable(T other) {
return is(other);
}
}
}

Methods with names of the type is_* did not exist before posting the sample in here and therefor will be removed...

EDIT

Even though I know how lower bounded wildcards work what I've been missing is that a Predicate&lt;? super Integer&gt; could be successfully casted to Predicate&lt;? super String&gt;.

Where:

Predicate&lt;? super String&gt; stringPredicate = (Predicate&lt;? super String&gt;)Filters.is_tClass(Integer.class, 4);
Predicate&lt;? super Server&gt; serverPredicate = (Predicate&lt;? super Server&gt;)Filters.is_comparable(5);
Collection&lt;Integer&gt; result = Stream.of(1, 2, 3)
.filter((Predicate&lt;? super Integer&gt;)stringPredicate)
.filter((Predicate&lt;? super Integer&gt;)serverPredicate)
.filter(Filters.is(new Object()))
.collect(Collectors.toSet());

results in [] empty resultset.

答案1

得分: 5

以下是翻译好的内容:

> 在这里,应用了两个断言,其中两者都不符合 <? super String>

这是不正确的:这两个断言确实消耗了一个 Object,而 ObjectString 的父类。

&lt;? super String&gt; 不能与 &lt;? extends String&gt; 混淆。

英文:

> Here, two predicates are applied where both of them aren't <? super String> compliant

It's not true: the 2 predicates do consume an Object, which is the parent of String.

&lt;? super String&gt; must not be confused with &lt;? extends String&gt;.

huangapple
  • 本文由 发表于 2020年10月26日 19:40:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/64536406.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定