【问题标题】:Invalid method reference for overloaded method with different arities具有不同参数的重载方法的方法引用无效
【发布时间】:2014-04-08 18:56:39
【问题描述】:

尝试编译表达式Comparator.comparing(String::toLowerCase) 时,Java 编译器返回错误。有关详细信息,请参阅以下问题:

Why Comparator.comparing doesn't work with String::toLowerCase method reference?


我已尝试尽可能减少问题。特别是,我已经删除了几乎所有对其他类的依赖。 main 方法包含两个方法调用。第一条语句编译没有错误,而第二条语句产生错误。

interface Fun<T, R> { R apply(T t); }

public final class Foo {
    public static void main(String... args) {
        invoke(Foo::bar); // OK
        invoke(Foo::baz); // ERROR
    }
    private static <T, U> void invoke(Fun<T, U> f) { }
    private String bar() { return null; }
    private String baz() { return null; }
    private String baz(Integer i, Integer j) { return null; }
}

这很奇怪,因为第二个 baz 方法不应该在这种情况下适用,因为参数数量不匹配。我看过 JLS8 (15.13)。然而,这并没有帮助,因为方法引用的规则相当复杂。

问:为什么第二种情况会出现编译错误?根据 JLS,真的应该存在编译错误吗?根据其他问题的一些cmets,Netbeans中没有编译错误。


作为参考,我使用的是 JDK8 版本 1.8.0-b132。如果在命令行上编译程序,编译器会显示以下错误信息:

$ /opt/jdk8/bin/javac Foo.java
Foo.java:6: error: incompatible types: cannot infer type-variable(s) T,U
        invoke(Foo::baz); // ERROR
              ^
    (argument mismatch; invalid method reference
      no suitable method found for baz(Object)
          method Foo.baz() is not applicable
            (actual and formal argument lists differ in length)
          method Foo.baz(Integer,Integer) is not applicable
            (actual and formal argument lists differ in length))
  where T,U are type-variables:
    T extends Object declared in method <T,U>invoke(Fun<T,U>)
    U extends Object declared in method <T,U>invoke(Fun<T,U>)
Foo.java:6: error: invalid method reference
        invoke(Foo::baz); // ERROR
               ^
  non-static method baz() cannot be referenced from a static context
2 errors

【问题讨论】:

  • 除了“Java 不够聪明,无法确定这里只有一个重载适用”之外,我不确定您期望什么答案。
  • 实际上,我正计划查看 JLS,看看我是否可以弄清楚规则以及它们在这种情况下如何应用。如果幸运的话,我应该在 11 月之前得到答案。
  • 我认为 JLS 规定方法引用必须是唯一的。

标签: java type-inference java-8 method-reference


【解决方案1】:

JLS8 (15.13) 令人困惑,但它确实显示了与您类似的示例,说明它们在搜索中存在歧义,无法解决。

对于您的示例,Intellij 说 invoke(Foo::baz); 是一个 循环推理我认为更多地与 invoke 的组合有关,也需要推断一个类型作为Foo::baz

这可以通过为invoke 函数指定一个类型来解决,类似于 JSL(15.13.1 示例)

搜索足够聪明,可以忽略所有适用方法(来自两个搜索)都是实例方法的歧义:

Foo.&lt;Foo,String&gt;invoke(Foo::baz); -- 相当于,我想使用Foovoid 方法返回String 又名String baz()

interface Fun<T, R> { R apply(T t); }
interface Fun2<T,U,R> { R apply(T t, U u); }

public final class Foo {
    public static void main(String... args) {
        invoke(Foo::bar); // OK
        Foo.<Foo,String>invoke(Foo::baz); // NO ERROR
        Fun2<Foo, Integer, String> f2 = Foo::baz; // Overloaded method baz
    }
    private static <T, U> void invoke(Fun<T, U> f) { }
    private String bar() { return null; }
    private String baz() { return null; }
    private String baz(Integer i) { return null; } 
}

我同意你的观点,baz(Integer i) 不是invoke 的有效参数,除非将其设为静态或来自Foo 的实例。我猜如果方法重载并且它试图推断类型,搜索算法就会退出。因为它只使用一个方法签名。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多