The following code creates a Collector
that produces an 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);
}
}
The codes compiles under the ecj compiler:
$ 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
Under javac however:
$ 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
If I change the offending line to the following, the code compiles under both compilers:
return Collectors.collectingAndThen(toSortedSet(), (SortedSet<T> p) -> Collections.unmodifiableSortedSet(p));
Is this a bug in ecj, javac or an underspecification that allows for both behaviours?
Javac behaves the same in java 9 and 10.
Oracle has accepted this as a compiler bug.
Interestingly, it compiles without the need for toSortedSet
:
public static <T extends Comparable<T>> Collector<T, ?, SortedSet<T>> toUnmodifiableSortedSet() {
return Collectors.collectingAndThen(Collectors.toCollection(TreeSet::new), Collections::<T>unmodifiableSortedSet);
}
It also compiles if you explicitly pass T
to toSortedSet
(removing static
and using this.<T>toSortedSet()
works just as well):
public static <T extends Comparable<T>> Collector<T, ?, SortedSet<T>> toUnmodifiableSortedSet() {
return Collectors.collectingAndThen(Test.<T>toSortedSet(), Collections::<T>unmodifiableSortedSet);
}
Regarding your question as to why it doesn't compile as is, I suspect it has to do with the capture types not being the same between both methods, and toSortedSet
needs the same exact type of T
used in toUnmodifiableSortedSet
(as you define a generic type T
for both methods).
I further believe that's the reason, as you can define a generic type T
for your class
and use it in both methods (if you remove 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);
}
}
And the above compiles and runs just fine.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With