Why can't java infer this type?

95 Views Asked by At

I was trying to mimic some pattern matching behaviour in java:

interface Result<T>
{
    <U> U eliminate(Function<T, U> f, BiFunction<Integer, String, U> g);
}

record Success<T>(T value) implements Result<T>
{
    @Override public <U> U eliminate(Function<T, U> f, BiFunction<Integer, String, U> g)
    {
        return f.apply(value);
    }
}

record Failure<T>(int errCode, String message) implements Result<T>
{
    @Override public <U> U eliminate(Function<T, U> f, BiFunction<Integer, String, U> g)
    {
        return g.apply(errCode, message);
    }
}
public class Main
{
    public static void main(String[] args)
    {
        Result<Double> result = Result.success(4.0);

        var log = result.eliminate(
            value -> "Success: " + value,
            (code, message) -> "Failure: " + code + " " + message
        );

        System.out.println(log);
    }
}

The above would compile and run to output "Success: 4.0". However when omitted the intermediate variable log and directly println its definition, unless explicitly stating the return type of eliminate i.e. result.<String>eliminate(...), java would suddenly not know what to do:

public class Main
{
    public static void main(String[] args)
    {
        Result<Double> result = Result.success(4.0);

        System.out.println(result.eliminate(
            value -> "Success: " + value,
            (code, message) -> "Failure: " + code + " " + message
        ));
    }
}
% javac -Xdiags:verbose Main.java
Main.java:7: error: reference to println is ambiguous
        System.out.println(result.eliminate(
                  ^
  both method println(char[]) in PrintStream and method println(String) in PrintStream match
Main.java:7: error: method println in class PrintStream cannot be applied to given types;
        System.out.println(result.eliminate(
                          ^
  required: char[]
  found:    String
  reason: argument mismatch; inference variable U has incompatible bounds
      upper bounds: char[],Object
      lower bounds: String
  where U,T are type-variables:
    U extends Object declared in method <U>eliminate(Function<T,U>,BiFunction<Integer,String,U>)
    T extends Object declared in interface Result
2 errors

Edit: definition of Result.success

static <T> Result<T> success(T value) { return new Success<>(value); }
1

There are 1 best solutions below

3
Dan On

Since you seem to be returning the 'U' type. I'd switch out your 'type' and make it

interface Result<U>

record Success<U>(U value) implements Result<U>

Or just make it String since that's exactly what you're looking to return.

Also, from a readability perspective, I'd create a 'Response' class that maps the value to message. If you're using Spring, you could use a ResponseEnity. It makes it easier to read.