Search code examples
javatype-inference

Java type inference not working as expected


I seem to have managed to break Java's type inference, with the following code:

public static void main (String[] args)
{
    final VirtualFS vfs = new VirtualFS();
    vfs.addDirHandler(vf -> {
        // top level directory only,
        // return a list of all tags
        return Arrays.stream("@tag_one @tag_two @tag_three".split(" +"))
                .map(str -> (vf.getName() + str))
                .map(str -> new VirtualFile(str)) // <-- here
                .toList();
    });
    ...
}

The above works fine, but if I change the marked line to:

                .map(VirtualFile::new) // <-- here

... then it refuses to compile (although the IDE doesn't complain about it). I can also fix the problem by adding a (String) cast to the previous .map operation.

The .getName() method in VirtualFile returns a String. Java version is 17, my IDE is Intellij IDEA. Like I said, I can solve the problem with a cast, but I'm curious as to what's going on to confuse the type inference.

The compiler error I get is:

java: incompatible types: invalid constructor reference
incompatible types: java.lang.Object cannot be converted to java.lang.String

Minimum reproducible example:

import java.util.function.Function;
import java.util.*;

public class VirtualFile
{
    private final String name;

    public VirtualFile(String name)
    {
        this.name = name;
    }

    public String getName () { return this.name; }

    private static class VirtualFS
    {
        public void addDirHandler (Function<VirtualFile, List<VirtualFile>> fn)
        {
        }
    }

    public static void main (String[] args)
    {
        final VirtualFS vfs = new VirtualFS();
        vfs.addDirHandler(vf -> {
            // top level directory only,
            // return a list of all tags
            return Arrays.stream("@tag_one @tag_two @tag_three".split(" +"))
                    .map(str -> vf.getName() + str)
                    .map(VirtualFile::new)
                    .toList();
        });
    }
}

Solution

  • This seems like the compiler bug JDK-8268312, where if you call a method taking a Function like (Stream.map) in the lambda parameter of another method that takes a Function (like addDirHandler), the type information of the former call gets lost.

    This is fixed in Java 20.