Vararg方法覆盖/重载混乱

时间:2016-06-12 12:03:49

标签: java overloading variadic-functions override method-overriding

计划1

class B
{
 public void m1(int x)
{
 System.out.println("Super class");
}
}

class A extends B
{
 public void m1(int... x)
{
 System.out.println("Sub class");
}
}

class test1
{
public static void main(String args[])
{
  B b1 = new B();
  b1.m1(10);

  A a = new A();
  a.m1(10);

  B b2 = new A();
  b2.m1(10);
}
}

输出:

  1. 超级课程
  2. 超类(无法理解为什么超类?!)
  3. 超类(无法理解为什么超类?!)
  4. 计划2:

    class B
    {
     public void m1(int... x)
    {
     System.out.println("Super class");
    }
    }
    
    class A extends B
    {
     public void m1(int x)
    {
     System.out.println("Sub class");
    }
    }
    
    class test1
    {
    public static void main(String args[])
    {
      B b1 = new B();
      b1.m1(10);
    
      A a = new A();
      a.m1(10);
    
      B b2 = new A();
      b2.m1(10);
    }
    }
    

    输出:

    1. 超级课程
    2. 子类(无法理解子类的原因?!)
    3. 超类(无法理解为什么超类?!)
    4. 大家好,有人可以解释是否存在覆盖/超载导致输出?

3 个答案:

答案 0 :(得分:11)

问题中根本没有覆盖,只是重载覆盖会导致A使用相同签名定义一个方法作为B中的相应方法(例如,如果它们都有{{1} }})。您在两个示例中都没有这样做,因为参数类型不同(m1(int)int)。

方法签名解析的机制(选择要使用的重载)由JLS §15.12.2: Compile-Time Step 2: Determine Method Signature涵盖。 Java编译器将根据引用类型(在您的情况下,变量的类型)在定义的接口上选择最具体的方法,如果有&#39任何歧义。

请注意,它是重要的引用类型,而不是引用所引用的对象的类型。引用的类型(在您的情况下为变量)是定义对象的接口的类型。 (这是"接口"在通用OOP意义上,而不是以它命名的Java特定事物。)编译器只能从该接口中可用的方法中进行选择。

有了这样的背景,原因很清楚:

计划1:

  1. 超级 - 因为int...的类型为b1B只有Bm1(int)与之匹配
  2. 超级 - 因为m1(10)的类型为a; A同时拥有A(来自m1(int)),还拥有自己的B;前者更具体,因此使用m1(int...)' s B
  3. 超级 - 因为m1(int)的类型为b2B只有B
  4. 计划2:

    1. 超级 - 因为m1(int)的类型为b1,而B只有B,而不是m1(int...)。< / LI>
    2. 子类 - 因为m1(int)的类型为aA同时包含A(来自m1(int...)),也是它自己的B;后者更具体,因此使用了m1(int)&#39; A
    3. 超级 - 因为m1(int)的类型为b2,而B只有B,而不是m1(int...)。< / LI>

答案 1 :(得分:5)

您的任何示例都没有覆盖,因为重写需要具有相同名称和签名的方法才能出现在超类和子类中。您的大多数示例都没有方法重载,因为只有一个m1版本可供编译器选择。

在第一个代码段中,编译时类型B的引用只能看到m1(int x)。编译时类型A的引用会看到两种方法,但m1(int x)仍然是首选,因为它与您的方法调用更好匹配(只有在没有方法时才能在方法重载分辨率中选择varargs的方法)与传递的参数匹配的其他方法,不包含varargs)。因此,在所有三种情况下都会调用超类方法。

在第二个片段中,编译时类型B的引用只能看到m1(int... x),这就是在第一和第三种情况下调用该方法的原因。编译时类型A的引用可以看到这两种方法,而这次m1(int x)再次是首选,因此第二种情况会调用子类方法。

答案 2 :(得分:1)

首先让我解释一下vararg,它将帮助我更好地回答你的问题。我们使用vararg提供原始数组作为方法的参数。由elipsis(三点符号)表示,并且应该始终是该方法的最后一个参数。这就是为什么我们不能在方法中有多个变量参数。在调用方法时,我们可以给出参数的值,也可以忽略。如果方法重载了变量参数&amp;特定类型的参数然后将验证第一匹配类型的参数,如果匹配则将被调用。如果未找到匹配的参数类型,则将验证更高类型(加宽),如果找到则调用。最后,它将验证变量参数参数类型。优先顺序的验证是 -

  

相同的参数类型&gt;更高类型>变量参数

现在,回答你的问题。

在计划1中 - A是B的子类。因此,类A具有带有int参数类型和vararg类型的m1()方法的实现。 A的实例是调用m1()方法的子类实现,它是相同的参数类型原因相同参数类型的优先级高于vararg。

A a = new A();
a.m1(10); // will call higher priority m1() with same parameter type
B b2 = new A();
b2.m1(10); // super class has sub class Obj.Based on dynamic disptch concept, it will always call the method declared in super class

在程序2中 - A是B.的子类。只是方法在这里互换了方法。 B类具有带有vararg参数的m1()方法,而类A具有int类型参数。

 A a = new A();
 a.m1(10); // Class A has both m1() method with int and vararg.Will call m1() method with int paramter, as it has higher priority over vararg.

 B b2 = new A();
 b2.m1(10); // Storing sub class reference into super class. Dynamic dyspatch says with super class reference we can call only method declared in super class. Super class has m1() method with vararg, will be called.  

希望这会有所帮助。