Why is lambda return type not checked at compile time?

后端 未结 4 2054
小蘑菇
小蘑菇 2020-12-29 18:54

The used method reference has return type Integer. But an incompatible String is allowed in the following example.

How to fix the method <

相关标签:
4条回答
  • 2020-12-29 19:28

    Its the type inference that is playing its role here. Consider the generic R in the method signature:

    <R> Builder<T> with(Function<T, R> getter, R returnValue)
    

    In the case as listed:

    Builder.of(MyInterface.class).with(MyInterface::getLength, "I am NOT an Integer");
    

    the type of R is successfully inferred as

    Serializable, Comparable<? extends Serializable & Comparable<?>>
    

    and a String does imply by this type, hence the compilation succeeds.


    To explicitly specify the type of R and find out the incompatibility, one can simply change the line of code as :

    Builder.of(MyInterface.class).<Integer>with(MyInterface::getLength, "not valid");
    
    0 讨论(0)
  • 2020-12-29 19:44

    It is because your generic type parameter R can be inferred to be Object, i.e. the following compiles:

    Builder.of(MyInterface.class).with((Function<MyInterface, Object>) MyInterface::getLength, "I am NOT an Integer");
    
    0 讨论(0)
  • 2020-12-29 19:46

    In the first example, MyInterface::getLength and "I am NOT an Integer" helped to resolve the generic parameters T and R to MyInterface and Serializable & Comparable<? extends Serializable & Comparable<?>>respectively.

    // it compiles since String is a Serializable
    Function<MyInterface, Serializable> function = MyInterface::getLength;
    Builder.of(MyInterface.class).with(function, "I am NOT an Integer");
    

    MyInterface::getLength is not always a Function<MyInterface, Integer> unless you explicitly say so, which would lead to a compile-time error as the second example showed.

    // it doesn't compile since String isn't an Integer
    Function<MyInterface, Integer> function = MyInterface::getLength;
    Builder.of(MyInterface.class).with(function, "I am NOT an Integer");
    
    0 讨论(0)
  • 2020-12-29 19:50

    This answer is based on the other answers which explain why it does not work as expected.

    SOLUTION

    The following code solves the problem by splitting the bifunction "with" into two fluent functions "with" and "returning":

    class Builder<T> {
    ...
    class BuilderMethod<R> {
      final Function<T, R> getter;
    
      BuilderMethod(Function<T, R> getter) {
        this.getter = getter;
      }
    
      Builder<T> returning(R returnValue) {
        return Builder.this.with(getter, returnValue);
      }
    }
    
    <R> BuilderMethod<R> with(Function<T, R> getter) {
      return new BuilderMethod<>(getter);
    }
    ...
    }
    
    MyInterface z = Builder.of(MyInterface.class).with(MyInterface::getLength).returning(1L).with(MyInterface::getNullLength).returning(null).build();
    System.out.println("length:" + z.getLength());
    
    // YIPPIE COMPILATION ERRROR:
    // The method returning(Long) in the type BuilderExample.Builder<BuilderExample.MyInterface>.BuilderMethod<Long> is not applicable for the arguments (String)
    MyInterface zz = Builder.of(MyInterface.class).with(MyInterface::getLength).returning("NOT A NUMBER").build();
    System.out.println("length:" + zz.getLength());
    

    (is somewhat unfamiliar)

    0 讨论(0)
提交回复
热议问题