简体   繁体   中英

Generic type mismatch while using a Collector returned by the method - Java 17

I was experimenting with records and streams .

I've created these records to count the number of letters in a text.

record Letter(int code) {
    Letter(int code) {
        this.code = Character.toLowerCase(code);
    }
}

record LetterCount(long count) implements Comparable<LetterCount> {
    @Override
    public int compareTo(LetterCount other) {
        return Long.compare(this.count, other.count);
    }

    static Collector<Letter, Object, LetterCount> countingLetters() {
        return Collectors.collectingAndThen(
            Collectors.<Letter>counting(), 
            LetterCount::new);
    }
}

And here is the snippet where they're used:

final var countedChars = text.chars()
    .mapToObj(Letter::new)
    .collect(
        groupingBy(Function.identity(), 
            LetterCount.countingLetters()                      // compilation error
            // collectingAndThen(counting(), LetterCount::new) // this line doesn't produce error
        ));

The snippet shown above doesn't produce error if I comment out collectingAndThen() to be used as the downstream collector within the groupingBy() .

However, the compiler gets lost when I'm trying to use LetterCount.countingLetters() as the downstream collector.

I'm getting the following error message:

Exception in thread "main" java.lang.Error: Unresolved compilation problems: 
        Collector cannot be resolved to a type
        Type mismatch: cannot convert from Collector<Letter,capture#17-of ?,LetterCount> to Collector<Letter,Object,LetterCount>
        The method countingLetters() from the type LetterCount refers to the missing type Collector
        The method entrySet() is undefined for the type Object

Interface Collector has the following declaration:

public interface Collector<T,​A,​R>

Where the second generic type parameter A denotes the type of the mutable container which is used internally to accumulate the results of the reduction. This type is usually hidden by the implementations.

Your method countingLetters() declares the return type as follows:

Collector<Letter, Object, LetterCount>

And that implies the type of the mutable container of the collector returned by this method is expected to be an Object .

Reminder: generics are invariant , ie if you've said Object you have to provide only Object (not it's subtype, not unknown type ? , only the Object type itself).

And that is incorrect for several reasons:

  • All collectors built-in in the JDK are hiding their types of mutable containers. Collector conting that you are using in your code declares to return Collector<T,?,Long> . Under the hood, it makes use of summingLong , which in turn returns Collector<T,?,Long> although it internally utilizes long[] as a container . That is because it doesn't make sense to expose these implementation details. And as a consequence, generic parameters of the return type you've declared doesn't conform to the generic parameters of the collector that you are returning. Ie since you are given an unknown type ? , you can't declare to return an Object .

  • Even if you wouldn't use built-in collectors, but your own custom collector instead, it still will not be a very bright idea to expose the actual type of its container . Simply because sometime in the future, you might want to change it.

  • Object class is immutable, hence it's fruitless to use it as a container type ( if you would try to implement a custom collector ) because it is not capable to accumulate data.


The bottom line: the second generic parameter in the collector returned by the countingLetters() method isn't correct.

To fix it, you have to change the type of the mutable container to be either:

  • an unknown type ? which encompasses all possible types, ie expected that provided collector may have a mutable container of any type (and that's how all collectors in the JDK have been declared);
  • or to an upper bounded wildcard ? extends Object ? extends Object (which is basically a more verbose way to describe the unknown type ).
public static Collector<Letter, ?, LetterCount> countingLetters()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM