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

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

问题描述

我有以下一段代码

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无法正常工作

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的Java版本测试您的代码.

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天全站免登陆