Этот код компилируется с использованием ecj, но не javac. Это ошибка в ecj, javac или нет?

Следующий код создает 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 -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 или underspecification, которая допускает оба поведения?

Javac ведет себя одинаково в java 9 и 10.

Ответ 2

Интересно, что он компилируется без необходимости 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.<T>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 для обоих методов).

Я также считаю, что причина, поскольку вы можете определить общий тип T для своего class и использовать его в обоих методах (если вы удалите 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);
    }
}

И выше компилируется и работает отлично.