可变参数函数 - Java:编译时解析和"most specific method",因为它适用于变量 arity



有人可以帮助我理解JLS re:最具体方法的第15.12.2.5节吗?

(以下是JLS的粗打剪切和粘贴)

此外,在以下任一情况下,一个名为 m 的变量 arity 成员方法比另一个同名的变量 arity 成员方法更具体:

  • 一个成员方法有 n 个参数,另一个有 k 个参数,其中 n>= k。第一个成员方法的参数类型是 T1, . . . , Tn-1 , Tn[],另一个方法的参数类型是 U1, . . . , Uk-1, Uk[]。如果第二种方法是通用的,则让 R1 ...Rp p1,是它的形式类型参数,设 Bl 是 Rl 的声明边界,1lp,设 A1 ...Ap 是在初始约束下为此调用推断的实际类型参数 (§15.12.2.7) Ti <<Ui,1ik-1, Ti <<Uk, kin, let Si = Ui[R1 = A1, ..., Rp = Ap] 1ik;否则设 Si = Ui, 1ik。然后: 对于从 1 到 k-1 的所有 j,Tj <:Sj,并且, 对于从 k 到 n 的所有 j,Tj <:Sk,并且, 如果第二种方法是如上所述的通用方法,则 Al <:Bl[R1 = A1, ..., Rp = Ap], 1lp。
  • 一个成员方法具有 k 个参数,另一个成员方法具有 n 个参数,其中 n>= k。第一种方法的参数类型是U1,...,Uk-1,Uk[],另一种方法的参数类型是T1,....,Tn-1,Tn[]。如果第二种方法是通用的,则让 R1 ...Rp p1,是它的形式类型参数,设 Bl 是 Rl 的声明边界,1lp,设 A1 ...Ap 是在初始约束 Ui <<Ti、1ik-1、Uk <<Ti、kin、let Si = Ti[R1 = A1, ..., Rp = Ap] 1in;否则让 Si = Ti,1in。然后: 对于从 1 到 k-1 的所有 j,Uj <:Sj,并且, 对于从 k 到 n 的所有 j,英国<:Sj,并且, 如果第二种方法是如上所述的通用方法,则 Al <:Bl[R1 = A1, ..., Rp = Ap], 1lp。

忽略泛型问题,这是否意味着在决定一种方法是否比另一种方法更具体时,varargs 比子类型更重要,或者子类型比 varargs 更重要?我想不通。

具体示例:根据JLS compute()方法中,以下哪种方法"更具体"?

package com.example.test.reflect;
class JLS15Test
{
    int compute(Object o1, Object o2, Object... others) { return 1; }
    int compute(String s1, Object... others)            { return 2; }
    public static void main(String[] args) 
    {
        JLS15Test y = new JLS15Test();
        System.out.println(y.compute(y,y,y));
        System.out.println(y.compute("hi",y,y));
    }
}

我无法弄清楚哪个"更具体";输出打印

1
2

我很困惑如何解释结果。当第一个参数是 String 时,编译器选择具有更具体子类型的方法。当第一个参数是 Object 时,编译器选择具有较少数量的可选变量的方法。


注意:如果您没有阅读 JLS 的这一部分,并且您给出的答案取决于论点的类型,那么您没有帮助我。如果你仔细阅读JLS,除了与泛型相关的部分,"更具体"的定义取决于声明的参数,而不是实际的参数 - 这在JLS的其他部分发挥作用(目前找不到它)。

例如,对于固定Arity方法,compute(String s)会比compute(Object o)更具体。但我正在尝试理解 JLS re:变量 arity 方法的相关部分。

  1. 当你调用compute("hi",y,y)时,int compute(String s1, Object... others)更具体,因为String是Object的子类。

  2. int compute(Object o1, Object o2, Object... others)compute(y,y,y) 的唯一匹配项,因为第二个方法接收 String 作为第一个参数,并且JLS15Test不是 String 的子类

编辑

我的答案在于特定方法的基础知识,但您的代码只能编译,因为编译器能够以上述方式区分方法。

以下示例由于其歧义而甚至不会编译:

案例1:

int compute(Object o1, Object o2, Object... others) { return 1; }
int compute(Object s1, Object... others)            { return 2; }
public static void main(String[] args) 
{
    JLS15Test y = new JLS15Test();
    System.out.println(y.compute(y,y,y));
    System.out.println(y.compute("hi",y,y));
}

案例2:

int compute(String o1, Object o2, Object... others) { return 1; }
int compute(Object s1, String... others)            { return 2; }
public static void main(String[] args) 
{
    JLS15Test y = new JLS15Test();
    System.out.println(y.compute("hi","hi","hi"));
}

更多 编辑

前两次我没有很好地回答你的问题(我希望这次:))。

您正在谈论的实际案例如下所示:

public class Test {
    public static void main(String[] args)
    {
        Test t = new Test();
        int a = t.compute("t", new Test());
        System.out.println(a);
    }
    int compute(String s, Object... others) { return 1; }
    int compute(Object s1, Object others)   { return 2; }
}

在这种情况下,compute(Object s1, Object others)确实比compute(String s, Object... others)更具体(参数更少),因此输出确实会2

在多次阅读JLS之后,我终于觉得我理解了这一部分。

他们的意思是,如果有两种变量arity方法,为了决定哪种方法"更具体",您可以考虑扩展参数列表较短的方法,使其长度与较长的参数列表相同。

int compute(Object o1, Object o2, Object... others)
int compute(String s1, Object... others)

可以认为(仅用于"更具体"的目的)等同于

int compute(Object o1, Object o2, Object... others)
int compute(String s1, Object,    Object... others)
然后逐个

比较参数类型,后一种方法更具体。

(更严格地说,第一个有 n = 3, k = 2, n>= k,字符串<:对象 [字符串是对象的子类型],JLS 指示直接比较 j 在 1 和 k-1 之间的每个参数的类型 [比较短的长度小一个],将较短方法签名的 vararg 类型与较长方法的其余参数进行比较。

在以下情况下:

int compute(Object o1, Object o2, String... strings)
int compute(Object o1, String... strings)

这些将等同于(仅用于"更具体"的目的)与

int compute(Object o1, Object o2, String... strings)
int compute(Object o1, String,    String... strings)

后者更具体。

因此,变量arity

永远不会胜过子类型,以比较"更具体"的方法,这些方法都是可变的arity。

但是,在可变arity方法之前,始终首先考虑固定arity方法(JLS 15.12.2.2和15.12.2.3)。

第二个计算调用打印 2,因为在编译时已知文本"hi"是字符串,因此编译器选择第二个方法签名,因为字符串比对象更具体。

相关内容

最新更新