使用通用供应商和lambda进行类型检查

Pat*_*eer 6 java generics lambda java-8

我有两个通用方法,旨在强制调用者提供类型匹配的参数:

private <T> void compareValues(Supplier<T> supplier, T value) {
    System.out.println(supplier.get() == value);
}

private <T> void setValue(Consumer<T> consumer, T value) {
    consumer.accept(value);
}
Run Code Online (Sandbox Code Playgroud)

但是,在调用它们时,编译器对允许作为参数传递的内容有不同的理由:

compareValues(this::getString, "Foo"); // Valid, as expected
compareValues(this::getInt, "Foo");    // Valid, but compiler should raise error
compareValues(this::getString, 1);     // Valid, but compiler should raise error

setValue(this::setString, "Foo");      // Valid, as expected
setValue(this::setInt, "Foo");         // Type mismatch, as expected
setValue(this::setString, 1);          // Type mismatch, as expected


private String getString() {
    return  "Foo";
}

private int getInt() {
    return 1;
}

private void setString(String string) {
}

private void setInt(int integer) {
}
Run Code Online (Sandbox Code Playgroud)

怎么会?编译器是否过于笨拙以至于无法在此处正确推理类型,或者这是类型系统的一个特性?如果是这样,导致这种行为的规则是什么?另外,如果可能的话,如何在不添加人工参数的情况下创建compareValues的"类型安全"版本?

请注意,提供的方法只包含一个虚拟实现,并不反映我实际代码库中的代码.这里的重点仅仅是方法调用.

Mic*_*ael 4

其他人已经提到了为什么会发生这种情况,因此这里有一个解决该问题的解决方案。

如果您创建一个泛型类,将供应商的传递与参数的传递分开,则编译器没有机会选择交集类型:

public class Comparer<T>
{
    private final Supplier<T> supplier;

    Comparer(final Supplier<T> supplier)
    {
        this.supplier = supplier;
    }

    void compare(T value)
    {
        System.out.println(supplier.get() == value);
    }
}

new Comparer<>(this::getString).compare("Foo"); // Valid, as expected
new Comparer<>(this::getInt).compare("Foo"); // Invalid, compiler error
new Comparer<>(this::getString).compare(1);  // Invalid, compiler error
Run Code Online (Sandbox Code Playgroud)

通过分离此行为,您还可以Comparer执行潜在有用的操作,例如缓存Supplier.get().