【问题标题】:This code compiles using ecj but not javac. Is this a bug in ecj, javac or neither?此代码使用 ecj 而不是 javac 编译。这是 ecj、javac 中的错误还是两者都没有?
【发布时间】:2018-09-20 14:44:02
【问题描述】:

以下代码创建了一个Collector,它产生了一个UnmodifiableSortedSet

package com.stackoverflow;

import java.util.Collections;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.stream.Collector;
import java.util.stream.Collectors;

public class SOExample {

    public static <T extends Comparable<T>> Collector<T, ?, SortedSet<T>> toSortedSet() {
        return Collectors.toCollection(TreeSet::new);
    }

    public static <T extends Comparable<T>> Collector<T, ?, SortedSet<T>> toUnmodifiableSortedSet() {
        return Collectors.collectingAndThen(toSortedSet(), Collections::<T> unmodifiableSortedSet);
    }
}

代码在ecj编译器下编译:

$ java -jar ~/Downloads/ecj-3.13.101.jar -source 1.8 -target 1.8 SOExample.java
Picked up _JAVA_OPTIONS: -Duser.language=en -Duser.country=GB

然而在 javac 下:

$ javac -version
Picked up _JAVA_OPTIONS: -Duser.language=en -Duser.country=GB
javac 1.8.0_73

$ javac SOExample.java
Picked up _JAVA_OPTIONS: -Duser.language=en -Duser.country=GB
SOExample.java:16: error: method collectingAndThen in class Collectors cannot be applied to given types;
        return Collectors.collectingAndThen(toSortedSet(), Collections::<T> unmodifiableSortedSet);
                         ^
  required: Collector<T#1,A,R>,Function<R,RR>
  found: Collector<T#2,CAP#1,SortedSet<T#2>>,Collection[...]edSet
  reason: cannot infer type-variable(s) T#3
    (actual and formal argument lists differ in length)
  where T#1,A,R,RR,T#2,T#3 are type-variables:
    T#1 extends Object declared in method <T#1,A,R,RR>collectingAndThen(Collector<T#1,A,R>,Function<R,RR>)
    A extends Object declared in method <T#1,A,R,RR>collectingAndThen(Collector<T#1,A,R>,Function<R,RR>)
    R extends Object declared in method <T#1,A,R,RR>collectingAndThen(Collector<T#1,A,R>,Function<R,RR>)
    RR extends Object declared in method <T#1,A,R,RR>collectingAndThen(Collector<T#1,A,R>,Function<R,RR>)
    T#2 extends Comparable<T#2>
    T#3 extends Object declared in method <T#3>unmodifiableSortedSet(SortedSet<T#3>)
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Object from capture of ?
1 error

如果我将违规行更改为以下内容,代码将在两个编译器下编译:

return Collectors.collectingAndThen(toSortedSet(), (SortedSet<T> p) -> Collections.unmodifiableSortedSet(p));

这是 ecj、javac 中的错误还是允许这两种行为的规范不足?

Javac 在 java 9 和 10 中的行为相同。

【问题讨论】:

  • 它在不使用toSortedSet 的情况下也可以编译:return Collectors.collectingAndThen(Collectors.toCollection(TreeSet::new), Collections::&lt;T&gt;unmodifiableSortedSet);
  • “实际参数列表和形式参数列表的长度不同”我想不出这些列表会是什么。
  • 雅各布,伟大的观察,非常奇怪的行为!肯定是 javac 错误?
  • 它在我看来确实像一个 javac 错误 - 错误消息毫无意义。

标签: java javac ecj


【解决方案1】:

有趣的是,它编译时不需要toSortedSet

public static <T extends Comparable<T>> Collector<T, ?, SortedSet<T>> toUnmodifiableSortedSet() {
    return Collectors.collectingAndThen(Collectors.toCollection(TreeSet::new), Collections::<T>unmodifiableSortedSet);
}

如果您将T 显式传递给toSortedSet,它也会编译(删除static 并使用this.&lt;T&gt;toSortedSet() 也可以):

public static <T extends Comparable<T>> Collector<T, ?, SortedSet<T>> toUnmodifiableSortedSet() {
    return Collectors.collectingAndThen(Test.<T>toSortedSet(), Collections::<T>unmodifiableSortedSet);
}

关于您关于为什么它不能按原样编译的问题,我怀疑这与两种方法之间的捕获类型不同有关,并且toSortedSet 需要与T 中使用的完全相同类型toUnmodifiableSortedSet(当您为这两种方法定义泛型类型 T 时)。

我进一步相信这就是原因,因为您可以为您的 class 定义一个泛型类型 T 并在两种方法中使用它(如果您删除 static):

public class Test<T extends Comparable<? super T>> {
    public static void main(String[] args) throws Exception {
        System.out.println(Stream.of(5, 3, 4, 2, 1, 5, 4, 3, 2, 1)
            .collect(new Test<Integer>().toUnmodifiableSortedSet()));
    }

    public Collector<T, ?, SortedSet<T>> toSortedSet() {
        return Collectors.toCollection(TreeSet::new);
    }

    public Collector<T, ?, SortedSet<T>> toUnmodifiableSortedSet() {
        return Collectors.collectingAndThen(toSortedSet(), Collections::unmodifiableSortedSet);
    }
}

上面的编译和运行都很好。

【讨论】:

  • 谢谢Jacob,你认为这是javac的一个bug吗?
  • 我不这么认为;我认为这只是因为您为每种方法定义了两种不同的泛型类型T
  • 那么 ecj 不应该编译它吗?
  • 我认为不会,除非 ecj 隐含推断 toUnmodifiableSortedSet 中定义的 T 应该是 toSortedSet 中使用的那个。
  • @JacobG。我同意不假设,但目前甚至无法对错误消息进行解释。
【解决方案2】:

Oracle 已将其作为compiler bug 接受。

【讨论】:

    猜你喜欢
    • 2017-07-05
    • 2014-12-01
    • 1970-01-01
    • 2014-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-23
    • 1970-01-01
    相关资源
    最近更新 更多