【问题标题】:Inferred type does not conform to equality constraint error for unrelated variables推断类型不符合不相关变量的等式约束错误
【发布时间】:2019-04-03 12:52:26
【问题描述】:

我有以下代码

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 从终端执行该类 我收到以下错误:

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"); 行,那么代码将毫无问题地执行。

Java 版本(适用于 macOS):

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

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

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

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

【问题讨论】:

  • EmployeeMaxMin 类是否相关?
  • @AndyTurner EmployeeMaxMin 未使用,但如果我删除它们,我将无法重现错误
  • 我无法重现,该程序在这两种情况下都适用于我。我也在使用 JDK 12 的 build 33。
  • 我可以在 mac 上重现 build 12+33,但我不明白取消注释 var strs = List.of("abc"); 如何使它在您的情况下编译;或者您可以全部编译:使用javac 也失败了。您确定您在此处提供的示例吗?

标签: java javac java-12


【解决方案1】:

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

我简化了您的示例并将 Collectors.teeingPredicate.not 的实现集成到示例中,以便能够使用从 JDK 9 到 JDK 12 的 Java 版本测试您的代码。

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

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') 会更改某些版本的结果。

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

【讨论】:

  • 感谢您提供详细信息。您能否提供更多关于您尝试过的版本以及获得的结果的详细信息,以便我们更有效地重现?
  • @BrianGoetz 我试过“9.0.4”、“10.0.2”2018-07-17、“11-ea”2018-09-25、“11”2018-09-25、“ 11.0.1" 2018-10-16、"12-ea" 2019-03-19、"12" 2019-03-19,其中大部分都在 OpenJDK 和 Oracle 版本中。如前所述,结果是相当随机的模式,即使是同一版本的 OpenJDK 和 OracleJDK 有时也会不一致,然后,细微的变化,例如在命令行中插入注释或指定另一个源文件,可以将结果完全改变为另一个随机图案。我认为,确切的模式并不真正相关。
  • 谢谢。你有什么感觉,即使保持编译器版本和程序不变,结果仍然是不确定的?
  • @BrianGoetz 不在我的测试中。给定一个特定的设置,结果似乎是可重现的。唯一的例外是在 Netbeans 内部,但我想,这是因为它以两种不同的设置运行编译器,一种用于在编辑器中突出显示语法,另一种用于在后台实际构建。
猜你喜欢
  • 1970-01-01
  • 2017-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多