在 Java 中向下转型 + 使用可变参数调用方法

Muk*_*nka 2 java variadic-functions downcast

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

test Icecream
test Faloodeh 
test Faloodeh:  Faloodeh
test Faloodeh:  Faloodeh
Run Code Online (Sandbox Code Playgroud)

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

test Faloodeh 
test Faloodeh 
test Faloodeh:  Faloodeh
test Faloodeh:  Faloodeh
Run Code Online (Sandbox Code Playgroud)

我想知道为什么会这样。

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

Run Code Online (Sandbox Code Playgroud)

**编辑- ** 感谢您的回答。请帮我解决另一个疑问。我将代码更改为 -

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

Run Code Online (Sandbox Code Playgroud)

现在这给出了以下输出 -

test Icecream
test Icecream
test Faloodeh:  Faloodeh
test Faloodeh:  Faloodeh
Run Code Online (Sandbox Code Playgroud)

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

Swe*_*per 5

这里的关键点是,改变displayName(String... s)displayName(String s)原因displayName(String s)的方法Faloodeh覆盖其超类中的方法。

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

在 Java 中,方法调用的解析大致分为三个步骤(有关更多信息:JLS §15.12,我还在此处进行了更详细的解释):

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

关于您的编辑:

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

  • displayName(String...)Faloodeh, and 中声明;
  • displayName(String) 这是继承的。

在这种情况下,编译器总是倾向于没有变量 arity -的重载displayName(String)。这在JLS §15.12.2 中有明确规定。特别是,步骤 2 进一步分为三个子步骤。第一个子步骤尝试在不允许可变元方法的情况下找到方法,如果任何子步骤找到任何方法,则跳过其余子步骤。