推断类型不符合无关变量的等式约束错误 [英] Inferred type does not conform to equality constraint error for unrelated variables

查看:28
本文介绍了推断类型不符合无关变量的等式约束错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下一段代码

public class TeeingCollector {

    public static void main(String[] args) {
        //        var strs = List.of("abc");
        var dividedStrings = Stream.of("foo", "hello", "bar", "world")
            .collect(Collectors.teeing(
                        Collectors.filtering((String s) -> s.length() <= 3, Collectors.toList()),
                        Collectors.filtering((String s) -> s.length() > 3, Collectors.toList()),
                        List::of
                        ));
        System.out.println(dividedStrings);
    }
    private static class Employee {
        boolean isActive;

        public Employee(boolean isActive) {
            this.isActive = isActive;
        }

        public boolean isActive() {
            return isActive;
        }

        @Override
            public String toString() {
                return "Employee{" +
                    "isActive=" + isActive +
                    '}';
            }
    }
    private static class MaxMin {
        int max;
        int min;

        MaxMin(int max, int min) {
            this.max = max;
            this.min = min;
        }

        @Override
            public String toString() {
                return "MaxMin{" +
                    "max=" + max +
                    ", min=" + min +
                    '}';
            }
    }
}

如果我使用 java src/TeeingCollector.java 从终端执行该类我收到以下错误:

If I execute that class from terminal with java src/TeeingCollector.java I get the following error:

src/TeeingCollector.java:14: error: incompatible types: inferred type does not conform to equality constraint(s)
            .collect(Collectors.teeing(
                    ^
inferred: List<String>
equality constraints(s): List<Object>,R
where R,T,A are type-variables:
R extends Object declared in method <T,A,R>filtering(Predicate<? super T>,Collector<? super T,A,R>)
T extends Object declared in method <T,A,R>filtering(Predicate<? super T>,Collector<? super T,A,R>)
A extends Object declared in method <T,A,R>filtering(Predicate<? super T>,Collector<? super T,A,R>)
1 error
error: compilation failed

如果我取消注释行 var strs = List.of("abc"); 则代码执行没有任何问题.

If I uncomment the line var strs = List.of("abc"); then the code is executed without any problem.

Java 版本(适用于 macOS):

Java version (for macOS):

OpenJDK Runtime Environment (build 12+33)
OpenJDK 64-Bit Server VM (build 12+33, mixed mode, sharing)

使用以下版本(较旧)运行相同的代码不会产生错误

running the same code with the following version (older) yields no errors

OpenJDK Runtime Environment (build 12-ea+23)
OpenJDK 64-Bit Server VM (build 12-ea+23, mixed mode, sharing)

注意:如果我编译它然后运行,我在 macOS 的两个构建中都没有任何错误,所以似乎只有 java TeeingCollector.java 不是't 工作正常

Note: if I compile it then run, I don't have any errors with both builds for macOS, so it seems that only java TeeingCollector.java isn't working properly

推荐答案

TL;DR 这显然是一个错误,因为编译器的行为取决于完全不相关的事情,包括 Java 语言之外的环境方面.

我简化了您的示例并将 Collectors.teeingPredicate.not 的实现集成到示例中,以便能够使用从 JDK 9 到JDK 12.

I simplified your example and integrated implementations of Collectors.teeing and Predicate.not into the example, to be able to test your code with Java versions from JDK 9 to JDK 12.

请注意,虽然我最初认为这是 var 的类型推断与右侧的泛型构造的某种交互,但更多的测试表明该问题甚至存在于为变量使用显式类型,允许在测试中包含 JDK 9.

Note that while I first thought that this was some kind of interaction of var’s type inference with the generic construct on the right-hand-side, more tests revealed that the problem even exists when using explicit types for the variables, which allowed to include JDK 9 in the tests.

import java.util.*;
import java.util.function.*;
import java.util.stream.*;

import java.util.stream.Collector;

public class Temp5 {

  public static void main(String[] args) {
    // List<Character> strs = List.of("abc");
    List<List<Character>> lettersAndNumbers = Stream.of('5', 't', 'o', '9', 'p', '1', 'h')
        .collect(teeing(
                Collectors.filtering(Character::isLetter, Collectors.toList()),
                Collectors.filtering(not(Character::isLetter), Collectors.toList()),
                List::of    
        ));
    }

    public static <T, R1, R2, R>
    Collector<T, ?, R> teeing(Collector<? super T, ?, R1> downstream1,
                              Collector<? super T, ?, R2> downstream2,
                              BiFunction<? super R1, ? super R2, R> merger) {
        return teeing0(downstream1, downstream2, merger);
    }

    private static <T, A1, A2, R1, R2, R>
    Collector<T, ?, R> teeing0(Collector<? super T, A1, R1> downstream1,
                               Collector<? super T, A2, R2> downstream2,
                               BiFunction<? super R1, ? super R2, R> merger) {
        Objects.requireNonNull(downstream1, "downstream1");
        Objects.requireNonNull(downstream2, "downstream2");
        Objects.requireNonNull(merger, "merger");

        Supplier<A1> c1Supplier = Objects.requireNonNull(downstream1.supplier(), "downstream1 supplier");
        Supplier<A2> c2Supplier = Objects.requireNonNull(downstream2.supplier(), "downstream2 supplier");
        BiConsumer<A1, ? super T> c1Accumulator =
                Objects.requireNonNull(downstream1.accumulator(), "downstream1 accumulator");
        BiConsumer<A2, ? super T> c2Accumulator =
                Objects.requireNonNull(downstream2.accumulator(), "downstream2 accumulator");
        BinaryOperator<A1> c1Combiner = Objects.requireNonNull(downstream1.combiner(), "downstream1 combiner");
        BinaryOperator<A2> c2Combiner = Objects.requireNonNull(downstream2.combiner(), "downstream2 combiner");
        Function<A1, R1> c1Finisher = Objects.requireNonNull(downstream1.finisher(), "downstream1 finisher");
        Function<A2, R2> c2Finisher = Objects.requireNonNull(downstream2.finisher(), "downstream2 finisher");

        Collector.Characteristics[] characteristics;
        Set<Collector.Characteristics> c1Characteristics = downstream1.characteristics();
        Set<Collector.Characteristics> c2Characteristics = downstream2.characteristics();
        EnumSet<Collector.Characteristics> c = EnumSet.noneOf(Collector.Characteristics.class);
        c.addAll(c1Characteristics);
        c.retainAll(c2Characteristics);
        c.remove(Collector.Characteristics.IDENTITY_FINISH);
        characteristics = c.toArray(new Collector.Characteristics[0]);

        class PairBox {
            A1 left = c1Supplier.get();
            A2 right = c2Supplier.get();
            void add(T t) {
                c1Accumulator.accept(left, t);
                c2Accumulator.accept(right, t);
            }
            PairBox combine(PairBox other) {
                left = c1Combiner.apply(left, other.left);
                right = c2Combiner.apply(right, other.right);
                return this;
            }
            R get() {
                R1 r1 = c1Finisher.apply(left);
                R2 r2 = c2Finisher.apply(right);
                return merger.apply(r1, r2);
            }
        }
        return Collector.of(PairBox::new, PairBox::add, PairBox::combine, PairBox::get, characteristics);
    }
    @SuppressWarnings("unchecked")
    static <T> Predicate<T> not(Predicate<? super T> target) {
        Objects.requireNonNull(target);
        return (Predicate<T>)target.negate();
    }
}

结果是灾难性的.每个版本都可以对代码的正确性有不同的看法,即使只是更改次要版本.好吧,即使使用 OpenJDK 而不是 Oracle 的发行版也会有不同的结果.此外,即使是对代码的最小更改也会对其产生影响.正如您所指出的,将注释更改为实际声明可能会改变结果,但即使删除注释也会改变某些 JDK 版本的结果.只需更改,例如使用 Stream.of('5', 't', 'o', '9', 'p', '1') 代替Stream.of('5', 't', 'o', '9', 'p', '1', 'h') 改变了某些版本的结果.

The results are disastrous. Every version can have a different opinion about the correctness of the code, even when just changing the minor version. Well, even using OpenJDK instead of Oracle’s distribution can have a different outcome. Additionally, even the smallest changes to the code can affect it. As you’ve noted, changing the comment into an actual declaration may change the outcome, but even removing the comment changes the outcome for some JDK versions. Just changing the values, like using Stream.of('5', 't', 'o', '9', 'p', '1') instead of Stream.of('5', 't', 'o', '9', 'p', '1', 'h') changes the outcome for some versions.

我的结论是,编译器实现中的某些内容取决于特定设置的稳定内容,但实际上是不可预测的,例如 HashMap 的迭代顺序.JDK 版本本身似乎也是其中的一部分.这也可以解释为什么在使用 MacOS 而不是 Linux 或 Windows 时结果可能会发生变化.即使将这个源文件与另一个不相关的源文件一起编译也会改变结果.

My conclusion is that something in the compiler implementation depends on something stable for a particular setup, but actually unpredictable, like the iteration order of a HashMap. And the JDK version itself seems to be part of it. This would also explain why the outcome may change when using MacOS instead of Linux or Windows. Even compiling this source file together with another, unrelated source file can change the result.

这篇关于推断类型不符合无关变量的等式约束错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆