有人可以帮我理解section 15.12.2.5 of the JLS re: most specific method吗?
(来自JLS的强行剪切和粘贴)
另外,一个名为m的变量arity成员方法比另一个同名的变量arity成员方法更具体,如果是:
- 一个成员方法具有n个参数,另一个具有k个参数,其中n> = k。第一个成员方法的参数类型是T1 ,. 。 。 ,Tn-1,Tn [],其他方法的参数类型是U1 ,. 。 。 ,Uk-1,英国[]。如果第二种方法是通用的,那么让R1 ... Rp p1成为它的形式类型参数,让Bl成为Rl的声明边界,1lp,让A1 ... Ap成为推断的实际类型参数(§15.12.2.7)对于在初始约束下的这种调用,Ti<< Ui,1ik-1,Ti<英国,亲和让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成为推断的实际类型参数(§15.12.2.7)对于在初始约束条件下的这种调用,Ui&lt;&lt; Ti,1ik-1,Uk <&lt; Ti,kin和Si = Ti [R1 = A1,...,Rp = Ap] 1in;否则让Si = Ti,1in。然后: 对于从1到k-1的所有j,Uj&lt;:Sj,和, 对于从k到n的所有j,Uk&lt;:Sj,以及 如果第二种方法是如上所述的通用方法,那么Al&lt;: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时,编译器选择的方法使用较少数量的可选varargs。
注意:如果您没有阅读JLS的这一部分,而您提供的答案取决于参数的类型,那么您并没有帮助我。如果你仔细阅读JLS,除了与泛型有关的部分,“更具体”的定义取决于声明的参数,而不是实际参数 - 这在其他部分发挥作用JLS(目前无法找到)。
e.g。对于固定的arity方法,compute(String s)
将比compute(Object o)
更具体。但我正在尝试理解JLS的相关部分:变量arity方法。
答案 0 :(得分:5)
int compute(String s1, Object... others)
时, compute("hi",y,y)
会更具体,因为String
是对象的子类。
int compute(Object o1, Object o2, Object... others)
是compute(y,y,y)
的唯一匹配,因为第二个方法接收String作为第一个参数,而JLS15Test
不是{的子类{1}}
修改强>
我的答案取决于具体方法的基础知识,但您的代码只能编译,因为编译器能够以上述方式区分方法。
以下示例甚至不会编译,因为它含糊不清:
案例1:
String
案例2:
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));
}
更多修改
前两次我没有得到你的问题(我希望我这次做:))。
您所谈论的实际案例将如下所示:
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)
。
答案 1 :(得分:1)
多次阅读JLS后,我终于想到了解这一节。
他们所说的是,如果有两个变量方法,为了决定哪个是“更具体”,你可以考虑扩展一个具有较短参数列表的方法,使其长度等于更长的一个。 e.g。
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&lt ;: Object [String是Object的子类型],JLS指示直接比较每个参数的类型对于1和k-1之间的j [比较短的长度小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从不胜过子类型。
然而,在变量方法之前,始终首先考虑固定方法(JLS 15.12.2.2和15.12.2.3)。
答案 2 :(得分:0)
第二个计算调用打印2,因为文字“hi”在编译时已知为String,因此编译器会选择第二个方法签名,因为String比Object更具体。