Java 方法重载 - 通用参数和同一继承树中的参数

2023-12-07

假设我有以下代码:

// Method acception generic parameter
public static <T> T foo(T para) {
    return para;
}

// Method accepting Integer parameter
public static Integer foo(Integer para) {
    return para + 1;
}

// Method accepting Number parameter
public static Number foo(Number para) {
    return para.intValue() + 2;
}

public static void main(String[] args) {
    Float f = new Float(1.0f);
    Integer i = new Integer(1);
    Number n = new Integer(1);
    String s = "Test";

    Number fooedFloat = foo(f);     // Uses foo(Number para)
    Number fooedInteger = foo(i);   // Uses foo(Integer para)
    Number fooedNumber = foo(n);    // Uses foo(Number para)
    String fooedString = foo(s);    // Uses foo(T para)

    System.out.println("foo(f): " + fooedFloat);
    System.out.println("foo(i): " + fooedInteger);
    System.out.println("foo(n): " + fooedNumber);
    System.out.println("foo(s): " + fooedString);
}

输出如下所示:

foo(f): 3
foo(i): 2
foo(n): 3
foo(s): Test

现在的问题是:

  1. foo(n) calls foo(Number para),很可能是因为n定义为Number,尽管它有一个Integer分配给它。那么我的假设是正确的,即采用哪种重载方法的决定是在编译时发生的,没有动态绑定? (关于问题static and dynamic捆绑)
  2. foo(f) uses foo(Number para), while foo(i) uses foo(Integer para). Only foo(s)使用通用版本。因此,编译器总是查找给定类型是否有非泛型实现,并且只有在没有时才回退到泛型版本? (关于问题generics)
  3. Again, foo(f) uses foo(Number para), while foo(i) uses foo(Integer para). Yet, Integer i也将是一个Number。那么总是采用继承树中“最外层”类型的方法吗? (关于问题遗产)

我知道这些是很多问题,并且该示例并非取自生产代码,但我只是想知道“后面发生了什么”以及为什么会发生这种情况。

另外,非常感谢任何指向 Java 文档或 Java 规范的链接,但我自己找不到它们。


确定在编译时调用哪个方法签名的规则在语言规范。特别重要的是关于选择的部分最具体的方法。以下是与您的问题相关的部分:

如果多个成员方法既可访问又适用于方法调用,则需要选择一个成员方法来为运行时方法分派提供描述符。 Java 编程语言使用以下规则:最具体的方法被选择。

...

One applicable method m1 is more specific than another applicable method m2, for an invocation with argument expressions e1, ..., ek, if any of the following are true:

  • m2 is generic, and m1 is inferred to be more specific than m2 for argument expressions e1, ..., ek by §18.5.4.

  • m2 is not generic, and m1 and m2 are applicable by strict or loose invocation, and where m1 has formal parameter types S1, ..., Sn and m2 has formal parameter types T1, ..., Tn, the type Si is more specific than Ti for argument ei for all i (1 ≤ in, n = k).

...

S型是更具体如果 S <: t>

在这种情况下,Integer比更具体Number因为Integer延伸Number,因此每当编译器检测到对foo需要一个变量declared类型的Integer,它将添加一个调用foo(Integer).

有关与第二种方法通用相关的第一个条件的更多信息,请参见本节。这有点冗长,但我认为重要的部分是:

当测试一种适用的方法时更具体与另一个方法(§15.12.2.5)相比,如果第二个方法是通用的,则有必要测试是否可以推断出第二个方法的类型参数的某些实例化,以使第一个方法比第二个方法更具体。

...

Let m1 be the first method and m2 be the second method. Where m2 has type parameters P1, ..., Pp, let α1, ..., αp be inference variables, and let θ be the substitution [P1:=α1, ..., Pp:=αp].

...

The process to determine if m1 is more specific than m2 is as follows:

...

If Ti is a proper type, the result is true if Si is more specific than Ti for ei (§15.12.2.5), and false otherwise. (Note that Si is always a proper type.)

这基本上意味着foo(Number) and foo(Integer)两者都比foo(T)因为编译器可以推断出泛型方法的至少一种类型(例如Number本身)使得foo(Number) and foo(Integer)更具体(这是因为Integer <: Number and Number <: Number) .

这也意味着在您的代码中foo(T)仅适用于传递 a 的调用(并且本质上是最具体的方法,因为它是唯一适用的方法)String.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 方法重载 - 通用参数和同一继承树中的参数 的相关文章

随机推荐