当前位置: 首页 > 知识库问答 >
问题:

使用基元类型的方法引用和函数接口专门化来解决重载问题

奚高扬
2023-03-14

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

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-

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错误,并试图遵循重载解决算法来解决它。我的感觉是代码应该被接受,因为从直觉上讲,ToIntFunctionToDoubleFunction更适合toInt。然而,我对JLS的理解是,它应该被拒绝,因为没有理由更具体。

我必须承认,我对JLS规范有点迷茫,希望能得到一些帮助。我首先想计算Main::double2int的类型,所以我看了15.13.2。方法引用的类型。它不定义类型,但定义类型在不同上下文中何时兼容:

如果T是函数接口类型(§9.8),且表达式与从T派生的基本目标类型的函数类型一致,则方法引用表达式在赋值上下文、调用上下文或转换上下文中与目标类型T兼容。

地面类型是ToIntFunction

然后我查看了过载分辨率,发现是15.12.2.5。对于编译时声明a的参数Main::toInt,选择对方法引用有特殊情况的最具体的方法来决定ToIntFunctionToDoubleFunction的两个重载中的哪一个更具体-

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

...

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

R2是空的。

R1

R1是基元类型,R2是引用类型,方法引用的编译时声明的返回类型是基元类型。

R1是引用类型,R2是基元类型,方法引用的编译时声明具有引用类型的返回类型。

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

这两个接口ToInFunctionToDoubleFunction的不同之处在于它们的返回类型是基本类型doubleint。对于基元类型,子句“R1”

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

对于两个函数接口返回原语,并且代码应该被拒绝为不明确的情况,似乎没有规则。然而,javac接受该代码,我希望它也会接受。所以我想知道这是否是JLS中缺失的一点。


共有1个答案

笪建章
2023-03-14

对于基元类型,子句"R1

事实并非如此double实际上是int的超类型。

第4.10段:

类型的超类型是通过对直接超类型关系的自反和传递闭包获得的,写为S

第4.10.1段:

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

double

float

long

超类型关系是直接超类型关系的自反和传递闭包,这意味着从(double

 类似资料:
  • 对于DynamicMessage,我想使用不同的处理方式,所以我尝试了 但它给出了编译错误 com中的“addMessageToResult(Message,Descriptor,Builder)”。谷歌。云垂直方向。电信公司。taap。数据流。数据摄取。常见的解析器。CSVParserDynamicMessage与com中的addMessageToResult(Message,Descripto

  • 有没有办法告诉Java不要试图从使用基元类型的方法引用中推断类型? 这是我写的一个方法,原因现在无关紧要: 现在,如果您将方法引用传递给返回原始类型的“isEquals”,该怎么办? 这一切都很好,但Java也会接受这种奇怪的用法: 这是因为编译器将推断类型参数T为"

  • 在C 11中,如何专门化一个函数模板,该模板使用decltype声明为“复杂”的尾部返回类型?以下内容适用于GCC,但在VC2013中产生了“错误C2912:显式专业化‘int f(void)’i不是功能模板的专业化”: 我说“复杂”是因为缺乏一个技术上精确的词,因为我不确定是什么造成了差异。例如,以下decltype使用不依赖于任何函数结果类型的内置操作,可以与模板专业化配合使用: 自动f()-

  • 具有以下3种重载 以下内容是否格式不正确? Clang选择重载#2,而gcc无法编译(演示) 移除过载#1时,双方同意选择过载#2(演示)。 删除重载#2时,gcc选择重载#1,clang编译失败(演示)

  • 顺便说一句,我确实编译了以下内容,但是专门化在运行时没有像预期的那样工作。基类型和派生类型最终要经历的非专用版本。 正确的语法是什么?

  • J.Bloch在《Effective Java》中提到,将varargs方法与基元类型一起使用是不安全的。简而言之,的返回类型为,这听起来很合理。现在我试图自己复制这种行为,但做不到: 我的问题是,为什么类型被推导为,而不是像他所说的那样被推导为?这是否意味着,在Java8中,关于varargs的问题不再相关了,如果我们不太关心性能,我们可以在任何地方安全地使用它们。