当前位置: 首页 > 面试题库 >

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

祁宝
2023-03-14
问题内容

假设我有以下代码:

// 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)调用foo(Number para),很可能是因为它n被定义为Number,即使它已Integer分配了。那么,我的假设是正确的,即采用哪种重载方法的决定是在编译时发生的,而没有动态绑定呢?(有关 静态动态 绑定的问题)
  2. foo(f)使用foo(Number para),而foo(i)使用foo(Integer para)。仅foo(s)使用通用版本。因此,编译器始终会查找给定类型是否存在非泛型实现,并且只有在不回到泛型版本的情况下,该编译器才会查找?(有关 泛型的 问题)
  3. 再次foo(f)使用foo(Number para),而foo(i)使用foo(Integer para)。但是,Integer i也将是Number。因此,是否总是采用继承树中“最外层”类型的方法?(关于 继承的 问题)

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

也非常感谢与Java文档或Java规范的任何链接,我自己找不到它们。


问题答案:

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

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

如果满足以下任一条件,则对于使用参数表达式… … 的调用,一种适用的方法比另一种适用的方法更具体:m1``m2``e1``ek

  • m2是通用的,由§18.5.4 推断比参数表达式… 更具体。m1``m2``e1``ek

  • m2不是通用的,并且是适用通过严格或松散调用,并且其中具有形参类型S1,…,Sn和具有形参类型Ť 1,…,T Ñ,S型我比更具体Ť
    我为参数对于所有 (1≤ ññ = ķ )。m1``m2``m1``m2``ei __

如果S <:T(第4.10节),则对于任何表达式,类型S 都比类型T 更具体

在这种情况下,由于Integer比extends 更具体,所以每当编译器检测到对该调用采用了 声明 为type
的变量的调用时,都会为添加一个调用。Number``Integer``Number``foo __Integer``foo(Integer)

本节将详细介绍与第二种方法有关的第一种条件有关的更多信息。这有点冗长,但我认为重要的部分是:

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

让是第一种方法,并是第二种方法。其中有一个类型参数p 1,…,P p,让α 1,…,α p是推断变量,并让θ是取代[P 1:=α 1,…,P
p:= α p ]。m1``m2``m2

确定是否比下面更具体的过程如下:m1``m2

如果T i是适当的类型,则对于e i(§15.12.2.5),如果S i比T i更具体,则结果为 true; 否则为 false 。(请注意,S
i始终是适当的类型。) __

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

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



 类似资料:
  • 问题内容: 这是我遇到的一个测试练习问题,希望您能帮助我理解概念 让Hawk成为Bird的子类。假设某个类有两个重载的方法void foo(Hawk h)和void foo(Bird b)。在声明Bird x = new Hawk()之后,将在调用foo(x)中执行哪个版本; 这是我到目前为止的代码,有人可以向我解释为什么foo(bird b)被执行吗? 问题答案: Java执行重载解析以选择方法

  • 本文向大家介绍解析Java继承中方法的覆盖和重载,包括了解析Java继承中方法的覆盖和重载的使用技巧和注意事项,需要的朋友参考一下 方法的覆盖 在类继承中,子类可以修改从父类继承来的方法,也就是说子类能创建一个与父类方法有不同功能的方法,但具有相同的名称、返回值类型、参数列表。   如果在新类中定义一个方法,其名称、返回值类型和参数列表正好与父类中的相同,那么,新方法被称做覆盖旧方法。   参数列

  • 我是一名新的Java开发人员,在Java中继承的概念定义我的类时遇到了一些困难。。。 在一方面,我有一个通用的类,名为车辆,它定义了几十年的属性(带有它们的getter/setters)。我有一个扩展车辆类的车辆1类。 另一方面,我有一个名为VehicleFactory的泛型类,它定义了一个静态方法,如下所示: 问题是我想创建一个Vehicle1Factory类,它有自己的makeResult方法

  • 本文向大家介绍php继承中方法重载(覆盖)的应用场合,包括了php继承中方法重载(覆盖)的应用场合的使用技巧和注意事项,需要的朋友参考一下 本文实例分析了php继承中方法重载(覆盖)的应用场合。分享给大家供大家参考。具体分析如下: 方法重载(override)/覆盖——在什么情况下使用:当父类知道所有的子类都需要用到一个方法,但父类不知道怎么去写这个方法时,就需要用到方法的重载。这时候,可以让子类

  • 问题内容: 我之所以学习,是因为我参加了考试,而大多数Java并没有很多问题,但是我偶然发现了一个我无法解释的规则。这是一个代码片段: 返回: 1 3 1 3 虽然我希望它会返回: 1 3 1 4 为什么a2的类型确定在AX中调用哪种方法? 我一直在阅读有关重载规则和继承的文章,但这似乎晦涩难懂,以至于我无法找到确切的规则。任何帮助将不胜感激。 问题答案: 这些方法调用的行为由Java语言规范(参

  • 问题内容: 我对在继承中使用私有方法感到困惑,例如: 基于上面的代码,我感到困惑的私有方法的继承,是继承了私有方法来?还是两个类中的say方法完全无关?由于代码在main()方法中运行时出错,因此似乎无法从中调用私有方法。 问题答案: 如果您想让子类访问需要保留的超类方法,那么您正在寻找的关键字。 只允许包含该成员的类访问该成员。 允许在类及其所有子类中访问成员。 允许任何人访问该成员。