Compiler complains "actual and formal argument lists differ in length", but they do not

When I compile the following code:

import com.google.common.collect.ImmutableMap;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collector;
import java.util.stream.Stream;

public class Testcase
{
    public static <T, K, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableMap1(
        Function<? super T, ? extends K> keyMapper,
        Function<? super T, ? extends V> valueMapper)
    {
        return null;
    }

    public static <T, K, V> MapCollectorBuilder<T, K, V, ? extends Map<K, V>, ImmutableMap<K, V>>
        toImmutableMap2(Function<? super T, ? extends K> keyMapper,
            Function<? super T, ? extends V> valueMapper)
    {
        return null;
    }

    public final class MapCollectorBuilder<T, K, V, A extends Map<K, V>, R extends Map<K, V>>
    {
        /**
         * @return a new collector
         */
        public Collector<T, A, R> build()
        {
            return null;
        }
    }

    public void main(String[] args)
    {
        Function<String, String> keyMapper = i -> i;
        Function<String, Integer> valueMapper = Integer::valueOf;

        ImmutableMap<String, Integer> map1 = Stream.of("1", "2", "3")
            .collect(Testcase.toImmutableMap1(keyMapper, valueMapper));

        MapCollectorBuilder<String, String, Integer, Map<String, Integer>, ImmutableMap<String, Integer>> builder
            = Testcase.
            <String, String, Integer, Map<String, Integer>, ImmutableMap<String, Integer>>toImmutableMap2(
                keyMapper, valueMapper);

        ImmutableMap<String, Integer> map2 = Stream.of("1", "2", "3").collect(builder.build());
    }
}

I get the following error:

Testcase.java:[45,35] method toImmutableMap2 in class Testcase cannot be applied to given types;
  required: java.util.function.Function<? super T,? extends K>,java.util.function.Function<? super T,? extends V>
  found: java.util.function.Function<java.lang.String,java.lang.String>,java.util.function.Function<java.lang.String,java.lang.Integer>
  reason: actual and formal argument lists differ in length

but as far as I can tell, the argument list is correct.

  1. Why does toImmutableMap1() compile while toImmutableMap2() does not?
  2. How can I make toImmutableMap2() compile properly?

UPDATE: If I replace the 5 type parameters with <String, String, Integer> I get this new compiler error:

incompatible types: Testcase.MapCollectorBuilder<String,String,Integer,CAP#1,ImmutableMap<String,Integer>> cannot be converted to Testcase.MapCollectorBuilder<String,String,Integer,Map<String,Integer>,ImmutableMap<String,Integer>>
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Map<String,Integer> from capture of ? extends Map<String,Integer>
Jon Skeet
people
quotationmark

Currently you're trying to specify 5 type arguments here:

Testcase.
    <String, String, Integer, Map<String, Integer>, ImmutableMap<String, Integer>>toImmutableMap2(

... but the method only has 3 type parameters:

public static <T, K, V> ComplexReturnTypeHere<...> toImmutableMap2

You can fix that by changing the call to:

Testcase.<String, String, Integer>toImmutableMap2(...)

But that changes the error message to:

Testcase.java:44: error: incompatible types: 
Testcase.MapCollectorBuilder<String,String,Integer,CAP#1,ImmutableMap<String,Integer>>
cannot be converted to 
Testcase.MapCollectorBuilder<String,String,Integer,Map<String,Integer>,
    ImmutableMap<String,Integer>>

That can in turn be fixed by changing the return type of the method declaration:

public static <T, K, V>
    MapCollectorBuilder<T, K, V, Map<K, V>, ImmutableMap<K, V>>
    toImmutableMap2(
        Function<? super T, ? extends K> keyMapper,
        Function<? super T, ? extends V> valueMapper)

The difference here is the second type argument in MapCollectorBuilder - Map<K, V> instead of ? extends Map<K, V>.

Alternatively, you could change the declaration of builder to:

MapCollectorBuilder<String, String, Integer, ? extends Map<String, Integer>, ImmutableMap<String, Integer>> builder

(Mind you, personally at this point it feels like it's falling into the realm of "incredibly hard to read and understand" code...)

people

See more on this question at Stackoverflow