Both Collector.of() are not correctly typed

Remi Forax forax at univ-mlv.fr
Tue Nov 13 14:47:54 UTC 2018


Last year,
a student of mine as remarked that the two variants of Collector.of() are not correctly typed (the wildcards are missing)
and obviously, this year it's one of my teaching assistant that has found exactly the same issue.
So let's fix this issue.

Adding the wildcards is both source and binary backward compatible in this case
- the methods of are static (so no overriding possible)
- the wildcards types are super types of the types of current version, so it will not break at use sites and
- the erased signature is the same so it's a binary backward compatible change.

The right code is
    @SuppressWarnings("unchecked")
    public static<T, R> Collector<T, R, R> of(Supplier<? extends R> supplier,
                                              BiConsumer<? super R, ? super T> accumulator,
                                              BinaryOperator<R> combiner,
                                              Characteristics... characteristics) {
        Objects.requireNonNull(supplier);
        Objects.requireNonNull(accumulator);
        Objects.requireNonNull(combiner);
        Objects.requireNonNull(characteristics);
        Set<Characteristics> cs = (characteristics.length == 0)
                                  ? Collectors.CH_ID
                                  : Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH,
                                                                           characteristics));
        return new Collectors.CollectorImpl<>((Supplier<R>)supplier, (BiConsumer<R, T>)accumulator, combiner, cs);
    }
and
    @SuppressWarnings("unchecked")
    public static<T, A, R> Collector<T, A, R> of(Supplier<? extends A> supplier,
                                                 BiConsumer<? super A, ? super T> accumulator,
                                                 BinaryOperator<A> combiner,
                                                 Function<? super A, ? extends R> finisher,
                                                 Characteristics... characteristics) {
        Objects.requireNonNull(supplier);
        Objects.requireNonNull(accumulator);
        Objects.requireNonNull(combiner);
        Objects.requireNonNull(finisher);
        Objects.requireNonNull(characteristics);
        Set<Characteristics> cs = Collectors.CH_NOID;
        if (characteristics.length > 0) {
            cs = EnumSet.noneOf(Characteristics.class);
            Collections.addAll(cs, characteristics);
            cs = Collections.unmodifiableSet(cs);
        }
        return new Collectors.CollectorImpl<>((Supplier<R>)supplier, (BiConsumer<R, T>)accumulator, combiner, (Function<A, R>)finisher, cs);
    }

You may ask why the code is safe given there are now some @SuppressWarnings("unchecked"), it's because for a functional interface,
the parameter types are contra-variant and the return type is covariant.

At one point in the future, perhaps JEP 300 [1] will be implemented, in that case we will be able to remove the @SuppressWarnings.

You may also notice that, we may want at the same time  replace the unmodifiableSet(EnumSet.of() + add) with Set.of(),
i've not done that change given it's not related to the typing issue.

cheers,
Rémi

[1] https://openjdk.java.net/jeps/300
 



More information about the core-libs-dev mailing list