【问题标题】:Java: compile-time resolution and "most specific method" as it applies to variable arityJava:编译时解析和“最具体的方法”,因为它适用于变量arity
【发布时间】:2011-05-16 21:06:41
【问题描述】:

谁能帮我理解section 15.12.2.5 of the JLS re: most specific method

(随后是来自 JLS 的大棒剪切和粘贴)

此外,一个名为 m 的可变参数成员方法比另一个同名可变参数成员方法更具体,如果:

  • 一个成员方法有 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
  • 一个成员方法有 k 个参数,另一个有 n 个参数,其中 n >= k。第一种方法的参数类型为 U1, . . . , Uk-1, Uk[], 其他方法的参数类型为 T1, . . ., Tn-1, Tn[]。如果第二种方法是泛型的,则令 R1 ... Rp p1 为其形式类型参数,令 Bl 为 Rl、1lp 的声明边界,令 A1 ... Ap 为推断的实际类型参数(第 15.12.2.7 节)对于初始约束下的此调用 Ui

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

具体示例:根据 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

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


注意:如果您没有阅读 JLS 的这一部分,并且您给出的答案取决于 参数的类型,那么您没有帮助我.如果您仔细阅读 JLS,除了与泛型相关的部分之外,“更具体”的定义取决于声明的参数,而不是实际的参数——这在JLS(暂时找不到)。

例如对于固定数量的方法,compute(String s) 将比compute(Object o) 更具体。但我试图了解 JLS re: variable arity methods 的相关部分。

【问题讨论】:

  • @David @Jason - 据我所知,您不能在第一种方法中将第一个参数的类型更改为 String 。因为它符合上述部分的最后一行 - we say that the method invocation is ambiguous, and a compile-time error occurs
  • 我认为 Jon Skeet 现在正在睡觉......
  • 啊。我只是想在我的原始帖子中放一个具体的例子,作为从 JLS 的措辞中去除抽象性的一种方式,相反,它所做的只是将注意力集中在具体的例子上,而不是 JLS 上。
  • 请查看编辑后的答案,我想我找到了一个符合您问题的案例。

标签: java variadic-functions jls


【解决方案1】:
  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 的子类

编辑

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

以下示例由于其歧义甚至无法编译:

案例一:

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。
  • +1 和大声笑,因为我完全错过了明显的答案,因为我认为 OP 会编写测试代码来传递满足两个方法签名的参数。
  • +1 了解详情。但是,您的最后一种情况是,对于“更具体”最适用的方法,原因不同——这不是因为它更具体,也不是因为它的参数更少,而是因为 compute(Object, Object) 是一个固定的arity方法,并且另一个是可变参数,首先考虑固定参数方法(JLS 规定 3 个阶段,第 1 阶段是完全匹配,第 2 阶段添加自动装箱/拆箱,第 3 阶段添加可变参数)。
  • @Jason - 我希望您在找到答案后与我们分享实际答案 :)
【解决方案2】:

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

他们的意思是,如果有两种可变参数方法,为了确定哪个“更具体”,您可以考虑扩展具有较短参数列表的方法,使其与较长的一个。例如

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,其中 String <: object is a subtype of jls j 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)

而后者更具体。

因此,在比较“更具体”的方法时,可变参数永远不会胜过子类型。

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

【讨论】:

    【解决方案3】:

    第二个计算调用打印 2,因为在编译时已知文字“hi”是一个字符串,因此编译器选择了第二个方法签名,因为 String 比 Object 更具体。

    【讨论】:

      猜你喜欢
      • 2011-08-29
      • 1970-01-01
      • 1970-01-01
      • 2012-11-20
      • 1970-01-01
      • 1970-01-01
      • 2018-03-19
      • 1970-01-01
      • 2011-11-27
      相关资源
      最近更新 更多