java +带有可变参数的调用方法中的向下转换

问题描述 投票:2回答:2

[当我调用a.displayName("Test")时,它将调用Icecream类的方法。 displayName(String...s)方法采用可变参数。输出-

test Icecream
test Faloodeh 
test Faloodeh:  Faloodeh
test Faloodeh:  Faloodeh

但是当我将方法更改为displayName(String s)(我已在代码中注释掉该部分)时,它将调用Faloodeh类的方法。新输出-

test Faloodeh 
test Faloodeh 
test Faloodeh:  Faloodeh
test Faloodeh:  Faloodeh

我想知道为什么会这样。

class Icecream{
    public void displayName(String...s){
        System.out.println(s[0]+" "+"Icecream");
    }
    /*public void displayName(String s){
        System.out.println(s+" "+"Icecream");
    }
    */
    public void describe(String s) {
        System.out.println(s+" "+"Icecream: Ice cream");
    }
}
class Faloodeh extends Icecream {
    public void displayName (String s){
        System.out.println(s+" "+"Faloodeh ");
    }

    public void describe (String s) {
        System.out.println(s+" "+"Faloodeh:  Faloodeh");
    }
}
 class Test {
    public static void main(String arg[]) {
       Icecream a=new Faloodeh ();
       Faloodeh b=( Faloodeh)a;
        a.displayName("test");
        b.displayName("test");
        a.describe("test");
        b.describe("test");
    }
}

**编辑-**感谢您的回答。如有其他疑问,请帮助我。我将代码更改为-

class Icecream{
    public void displayName(String s){
        System.out.println(s+" "+"Icecream");
    }
    /*public void displayName(String s){
        System.out.println(s+" "+"Icecream");
    }
    */
    public void describe(String s) {
        System.out.println(s+" "+"Icecream: Ice cream");
    }
}
class Faloodeh extends Icecream {
    public void displayName (String...s){
        System.out.println(s+" "+"Faloodeh ");
    }

    public void describe (String s) {
        System.out.println(s+" "+"Faloodeh:  Faloodeh");
    }
}
 class Test {
    public static void main(String arg[]) {
       Icecream a=new Faloodeh ();
       Faloodeh b=( Faloodeh)a;
        a.displayName("test");
        b.displayName("test");
        a.describe("test");
        b.describe("test");
    }
}

现在这给出以下输出-

test Icecream
test Icecream
test Faloodeh:  Faloodeh
test Faloodeh:  Faloodeh

正如大家所解释的,这里b是Faloodeh类的对象。 Faloodeh类的displayName(String...s)不会被覆盖。仍在输出中,它显示test Icecream为什么这样?

java variadic-functions downcast
2个回答
4
投票

这里的关键是将displayName(String... s)更改为displayName(String s)会导致displayName(String s)中的Faloodeh方法其超类中的方法。

Icecream.displayName(String... s)Faloodeh.displayName(String s)具有不同的签名,因此它们不会相互覆盖。但是将前者更改为接受一个String仅会导致它们具有相同的签名,这会导致覆盖。

在Java中,方法调用大致可通过三个步骤来解决(有关更多信息:JLS §15.12,我也将更详细地解释here):

    查找类以搜索适用的方法。这基于您要在其上调用方法的对象的编译时类型。在这种情况下,为aa的编译时间类型为Icecream,因此仅考虑Icecream的方法。请注意,由于displayName的编译时间类型为Faloodeh,因此在a中找不到Icecream方法。
  1. 根据您传递的参数确定要调用的方法重载。这里只有一种选择。与更改前后一样,displayName是唯一与您传递的参数兼容的重载。
  2. 根据您在其上调用方法的对象的运行时类型来确定要调用的方法的实现。 a的运行时类型为Faloodeh。更改之前,displayName不会在Faloodeh中被覆盖,因此它将调用超类实现。更改之后,displayName被覆盖,因此调用了Faloodeh中的实现。
  3. 关于您的编辑:

在这种情况下,由于b的编译时间类型为Faloodeh,因此要搜索的类别为Faloodeh(步骤1)。但是,有两种方法与您提供的参数匹配(步骤2):

    displayName(String...)中声明的[Faloodeh,和;
  • [继承的C0]。>
  • 在这种情况下,编译器

    始终支持无变量Arity的重载

  • -displayName(String)。这在displayName(String)中有明确规定。特别是,步骤2进一步分为三个子步骤。第一个子步骤尝试查找不允许使用可变Arity方法的方法,如果任何子步骤找到任何方法,那么将跳过其余的子步骤。
您的测试似乎表明您在玩多态。因此,您可能知道,在Java中,我们可以说您的对象具有两种2种类型:

    静态类型:基于变量的声明:JLS §15.12.2。 Object a的编译(即静态)类型为Icecream a = ...
  • 动态类:基于此变量的影响:Icecream
  • writing

  • 代码时,假设您仅使用静态类型。这意味着编译器知道您可以使用的类/字段/方法,并允许您使用它们。这就是为什么您可以编写类似:的代码的原因... a = new Faloodeh()
    而且不能写:

    Icecream a = new Icecream(); a.displayName("test");

    编译器知道您的Icecream类具有一个名为Icecream a = new Icecream();
    a.unknownMethod("test");
    的方法,该方法采用var-arg。它还知道存在displayName类。编译器知道此类可以具有自己的方法和字段,并且可以访问其父级的方法和字段。

    Faloodeh

    因此,基本上,当您在类中声明并实现方法时,其子级可以通过重新实现该方法来覆盖行为。这是您使用方法Faloodeh b = new Faloodeh();
    b.displayName("test");
    执行的操作。

    但是方法void describe(String s)是棘手的,因为您以相同的方式调用它,但是实际上,由于它们都使用了arg,因此它们并不相同。让我们尝试成为一个编译器。我编译了您的两个类,在这里创建了我创建的内容:

    displayName

    现在,在运行代码时,让我们看看将使用主行中的行真正调用哪些方法:

    Icecream => displayName | varargs Icecream => describe | String Fadooleh => displayName | String Faloodeh => describe | String

    为什么第一行调用方法Icecream a = new Faloodeh();
    Faloodeh b = (Faloodeh)a;
    a.displayName("test"); => Icecram => displayName | varargs
    b.displayName("test"); => Fadooleh => displayName | String
    a.describe("test"); => Faloodeh => describe | String
    b.describe("test"); => Faloodeh => describe | String
    而不是displayName(String...)?因为从静态上看,编译器看到您使用的是Icecream类型来调用displayName(String)方法,并且动态地,只有一个带有varargs的方法displayName,此方法从未被Faloodeh覆盖。因此,您可以直接调用Icecream displayName方法。

    如果您在Icecream中取消注释方法displayName,那么您将让Faloodeh接管,因为动态地,Faloodeh在displayName(String s)上有其自己的实现,这就是调用它的原因。

    希望有帮助。有关多态的更多信息:displayName(String)

    **编辑**这几乎是相同的原因。您的两个类都静态具有方法https://www.tutorialspoint.com/java/java_polymorphism.htm,另一个类具有displayName(String)

    [使用displayName(String...)时,它将首先与b.displayName("test")匹配,仅在您的displayName(String)对象中实现。因此,行为。

    例如,这将是不可能的:

    Icecream

    因为Icecream a = new Faloodeh()
    a.displayName("test", "test");
    对名为Icecream的方法一无所知。

    1
    投票
    您的测试似乎表明您在玩多态。因此,您可能知道,在Java中,我们可以说您的对象具有两种2种类型:
    © www.soinside.com 2019 - 2024. All rights reserved.