使用方法引用和原始类型的函数接口专门化进行重载解析

时间:2017-08-25 09:18:09

标签: java language-lawyer overload-resolution method-reference

假设我们有一个类和一个重载函数:

public class Main {
    static final class A {
    }

    public static String g(ToIntFunction<? extends A> f) {
        return null;
    }

    public static String g(ToDoubleFunction<? extends A> f) {
        return null;
    }
}

我希望用类型A的函数的方法引用来调用g - &gt; INT:

public class Main {
    static final class A {
    }

    public static String g(ToIntFunction<? extends A> f) {
        return null;
    }

    public static String g(ToDoubleFunction<? extends A> f) {
        return null;
    }

    private static int toInt(A x) {
        return 2;
    }

    public static void main(String[] args) {
        ToIntFunction<? extends A> f1 = Main::toInt;
        ToDoubleFunction<? extends A> f2 = Main::toInt;

        g(Main::toInt);
    }
}

这适用于javac,但不适用于eclipse ecj。我向ecj提交了一个错误报告,但我不确定这是否是一个ecj或javac错误,并尝试按照重载解析算法来解决它。我的感觉是代码应该被接受,因为ToIntFunctiontoIntToDoubleFunction更好地匹配是直观合理的。但是,我对JLS的解读是它应该被拒绝,因为没有理由让一个更具体。

我必须承认我在JLS规范中有点迷失并且会感激一些帮助。我首先想要计算Main::double2int的类型,所以我查看了15.13.2. Type of a Method Reference。它没有定义类型,但它定义了何时类型在不同的上下文中兼容:

  

方法引用表达式在赋值上下文中兼容,   如果T是a,则调用上下文或者使用目标类型T转换上下文   功能接口类型(§9.8)和表达式是一致的   从T。

派生的地面目标类型的函数类型

地面类型为ToIntFunction<A>ToDoubleFunction<A>toInt返回一个与double兼容的赋值的int,因此我得出结论,方法引用在调用上下文中可以与ToIntFunction<? extends A>ToDoubleFunction<? extends A>一起使用。这可以通过在主函数中接受ToIntFunction<? extends A>ToDoubleFunction<? extends A>的方法引用来验证。

然后我查看了重载决策,找到了15.12.2.5. Choosing the Most Specific Method,它有一个特殊的方法引用案例来决定ToIntFunctionToDoubleFunction的两个重载中哪一个对参数更具体{ {1}}编译时声明Main::toInt

  

如果T不是S的子类型且下列之一为真,则功能接口类型S比表达式e的功能接口类型T更具体(其中U1 ... Uk和R1是参数类型和返回类型为S的捕获函数类型,V1 ... Vk和R2是T)函数类型的参数类型和返回类型:

     

...

     

如果e是精确的方法参考表达式(§15.13.1),那么i)for   所有i(1≤i≤k),Ui与Vi相同,ii)以下之一   是的:

     

R2无效。

     

R1&lt;:R2。

     

R1是基本类型,R2是引用类型,而编译时   方法引用的声明的返回类型是a   原始类型。

     

R1是引用类型,R2是基本类型,而编译时   方法引用的声明的返回类型是a   参考类型。

第一个条件显然不匹配,因为R1和R2不是无效的。

两个接口A -> intToIntFunction的区别仅在于它们的原始类型ToDoubleFunctiondouble的返回类型。对于基本类型,根据类型的大小,在4.10.1中定义了“R1&lt ;: R2”子句。 intdouble之间没有关系,因此这种情况不会定义哪种类型更具体。

最后两点不合适,因为两个功能接口都没有引用类型的返回值。

当两个功能接口返回原语并且代码应该被拒绝为模糊时,似乎没有规则。但是,javac接受了代码,我希望它能够这样做。所以我想知道这是否是JLS中的缺失点。

1 个答案:

答案 0 :(得分:3)

  

对于基本类型,根据类型的大小,在4.10.1中定义了“R1&lt ;: R2”子句。 double和int之间没有关系,所以这种情况不能定义哪种类型更具体。

事实并非如此;事实上,doubleint的超类型。

Paragraph 4.10

  

一种类型的超类型是通过反身和传递获得的   关于直接超类型关系的闭包,写成S >₁ T,即   由本节后面给出的规则定义。我们写S :> T给   表示超类型关系在ST之间保持不变。

Paragraph 4.10.1

  

以下规则定义了之间的直接超类型关系   原始类型:

     

double >₁ float

     

float >₁ long

     

long >₁ int

超类型关系是直接超类型关系的自反和传递闭包,意味着(double >₁ float) ∧ (float >₁ long) ∧ (long >₁ int)跟随double :> int