Java:编译时解析和“最具体的方法”,因为它适用于变量arity

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

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

另外,一个名为m的变量arity成员方法比另一个具有相同名称的变量arity成员方法更具体:

  • 一个成员方法有n个参数,另一个成员方法有k个参数,其中n> = k。 第一个成员方法的参数类型是T1 ,. 。 。 ,Tn-1,Tn [],其他方法的参数类型是U1 ,. 。 。 ,Uk-1,英国[]。 如果第二种方法是通用的,那么让R1 … Rp p1成为它的forms类型参数,让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成为它的forms类型参数,让Bl成为Rl的声明边界,1lp,让A1 … Ap成为推断的实际类型参数(§15.12.2.7)对于这种在初始约束条件下的调用,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,Uk <:Sj,并且,如果第二种方法是如上所述的通用方法,则Al <:Bl [R1 = A1,…,Rp = Ap],1lp。

忽略问题generics,这是否意味着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时,编译器选择具有较少可选varargs的方法。


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

例如,对于固定的arity方法, compute(String s)将比compute(Object o)更具体。 但我试图理解JLS的相关部分:变量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作为第一个param,而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后,我终于想到我理解了这一部分。

他们所说的是,如果有两个变量方法,为了确定哪个是“更具体”,你可以考虑扩展一个具有较短参数列表的方法,使其长度与长一个相等。 。 例如

 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 [String是Object的子类型],JLS规定直接比较j的每个参数的类型在1和k-1 [比较短的长度小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)。

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