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

发布于 2024-11-07 10:59:01 字数 2145 浏览 0 评论 0原文

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

(以下是来自 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 并设 Si = Ui[R1 = A1, ..., Rp = Ap] 1ik;否则令 Si = Ui, 1ik。然后: 对于从 1 到 k-1 的所有 j,Tj <:Sj,并且, 对于从 k 到 n 的所有 j,Tj <:Sk,并且, 如果第二方法是如上所述的通用方法,则A1<: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 << Ti,1ik-1,Uk<< Ti, kin 并设 Si = Ti[R1 = A1, ..., Rp = Ap] 1in;否则令 Si = Ti, 1in。然后: 对于从 1 到 k-1 的所有 j,Uj <:Sj,并且, 对于从 k 到 n 的所有 j ,Uk <:Sj,并且, 如果第二方法是如上所述的通用方法,则A1<:Bl[R1=A1,...,Rp=Ap],1lp。

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

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

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


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

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

Could someone help me understand section 15.12.2.5 of the JLS re: most specific method?

(bludgeoned cut&paste from JLS follows)

In addition, one variable arity member method named m is more specific than another variable arity member method of the same name if either:

  • One member method has n parameters and the other has k parameters, where n >= k. The types of the parameters of the first member method are T1, . . . , Tn-1 , Tn[], the types of the parameters of the other method are U1, . . . , Uk-1, Uk[]. If the second method is generic then let R1 ... Rp p1, be its formal type parameters, let Bl be the declared bound of Rl, 1lp, let A1 ... Ap be the actual type arguments inferred (§15.12.2.7) for this invocation under the initial constraints Ti << Ui,1ik-1, Ti << Uk, kin and let Si = Ui[R1 = A1, ..., Rp = Ap] 1ik; otherwise let Si = Ui, 1ik. Then:
    for all j from 1 to k-1, Tj <: Sj, and,
    for all j from k to n, Tj <: Sk, and,
    If the second method is a generic method as described above then Al <: Bl[R1 = A1, ..., Rp = Ap], 1lp.
  • One member method has k parameters and the other has n parameters, where n >= k. The types of the parameters of the first method are U1, . . . , Uk-1, Uk[], the types of the parameters of the other method are T1, . . ., Tn-1, Tn[]. If the second method is generic then let R1 ... Rp p1, be its formal type parameters, let Bl be the declared bound of Rl, 1lp, let A1 ... Ap be the actual type arguments inferred (§15.12.2.7) for this invocation under the initial constraints Ui << Ti, 1ik-1, Uk << Ti, kin and let Si = Ti[R1 = A1, ..., Rp = Ap] 1in; otherwise let Si = Ti, 1in. Then:
    for all j from 1 to k-1 , Uj <: Sj, and,
    for all j from k to n , Uk <: Sj, and,
    If the second method is a generic method as described above then Al <: Bl[R1 = A1, ..., Rp = Ap], 1lp.

Ignoring the issue generics, does this mean varargs is more important than subtyping, or subtyping is more important than varargs, when deciding whether one method is more specific than another? I can't figure it out.

Concrete example: Which of the following compute() methods is "more specific" according to the JLS?

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));
    }
}

I can't figure out which is "more specific"; the output prints

1
2

I'm confused how to interpret the results. When the first argument was a String, the compiler picked the method with the more specific subtype. When the first argument was an Object, the compiler picked the method with the fewer number of optional varargs.


NOTE: If you are not reading this section of the JLS, and you are giving an answer that depends on the types of the arguments, you are not helping me. If you carefully read the JLS, other than the parts relating to generics, the definition of "more specific" depends on the declared arguments, not on the actual arguments -- this comes into play in other parts of the JLS (can't find it at the moment).

e.g. for fixed arity methods, compute(String s) would be more specific than compute(Object o). But I'm trying to understand the relevant section of the JLS re: variable arity methods.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

驱逐舰岛风号 2024-11-14 10:59:01
  1. 当您调用compute("hi",y,y)时,

    intcompute(String s1, Object...others)更加具体,因为String 是 Object 的子类。

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

编辑

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

由于含糊不清,以下示例甚至无法编译:

case 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));
}

case 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 other) 确实比compute(String s, Object... other) 更具体 (参数较少),因此输出实际上是 2

  1. int compute(String s1, Object... others) is more specific when you call compute("hi",y,y), since String is a subclass of Object.

  2. int compute(Object o1, Object o2, Object... others) is the only match for compute(y,y,y) because the second method receives String as first param, and JLS15Test is not a subclass of String

EDIT

My answer resides on the basics of specific methods, but your code only compiles because of the ability of the compiler to distinguish between the methods in the way described above.

The following examples will not even compile because of its ambiguity:

case 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));
}

case 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"));
}

More Edit

I didn't get your question well the first two times (and I hope I do this time :) ).

The actual case you are talking about will look like that:

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; }
}

In this case, compute(Object s1, Object others) is indeed more specific then compute(String s, Object... others) (has less parameters) so the output will be 2 indeed.

烟沫凡尘 2024-11-14 10:59:01

读了多次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 [比较短的长度少一],将较短方法签名的可变参数类型与较长方法的其余参数进行比较。)

在以下情况下:

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)。

After reading the JLS multiple times, I finally think I understand this section.

What they are saying is that if there are two variable-arity methods, for the purposes of deciding which is "more specific", you can consider the one with the shorter argument list to be extended so that is of equal length to the longer one. e.g.

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

can be considered (only for the purposes of "more specific") to be equivalent to

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

and then the argument types are compared, one by one, with the latter method being more specific.

(more rigorously, the first one has n = 3, k = 2, n >= k, with String <: Object [String is a subtype of Object] and the JLS dictates comparing the types directly of each parameter for j between 1 and k-1 [one less than the shorter length], comparing the vararg type of the shorter method signature with the remaining parameters of the longer method.)

In the following case:

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

these would be equivalent (only for the purposes of "more specific") to

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

and the latter more specific.

So variable-arity never trumps subtyping for the purposes of comparing "more specific" methods which are both of variable arity.

However, fixed-arity methods are always considered first (JLS 15.12.2.2 and 15.12.2.3) before variable-arity methods.

醉酒的小男人 2024-11-14 10:59:01

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

The second compute call prints 2 because the literal "hi" is known to be a String at compile time, so the compiler choses the second method signature because String is more specific than Object.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文