Java:调用一个调用重写方法的超级方法

时间:2011-01-04 15:47:57

标签: java inheritance override super

public class SuperClass
{
    public void method1()
    {
        System.out.println("superclass method1");
        this.method2();
    }

    public void method2()
    {
        System.out.println("superclass method2");
    }

}

public class SubClass extends SuperClass
{
    @Override
    public void method1()
    {
        System.out.println("subclass method1");
        super.method1();
    }

    @Override
    public void method2()
    {
        System.out.println("subclass method2");
    }
}



public class Demo 
{
    public static void main(String[] args) 
    {
        SubClass mSubClass = new SubClass();
        mSubClass.method1();
    }
}

我的预期输出:

  

子类方法1
  超类方法1   超类方法2

实际输出:

  

子类方法1
  超类方法1   子类方法2

我从技术上知道我已经覆盖了一个公共方法,但我认为因为我在调用super,所以super中的任何调用都会保留在super中,这种情况不会发生。关于如何实现它的任何想法?

13 个答案:

答案 0 :(得分:68)

关键字super不会“粘住”。每个方法调用都是单独处理的,因此即使您通过调用SuperClass.method1()进入super,也不会影响您将来可能进行的任何其他方法调用。

这意味着除非您正在使用SuperClass.method2()的实际实例,否则无法通过SuperClass.method1()直接从SubClass.method2()调用SuperClass

使用反射甚至无法达到预期效果(请参阅the documentation of java.lang.reflect.Method.invoke(Object, Object...))。

[编辑] 似乎仍有一些混乱。让我尝试不同的解释。

当您调用foo()时,实际上是在调用this.foo()。 Java只是让你省略this。在问题的示例中,this的类型为SubClass

因此当Java执行SuperClass.method1()中的代码时,它最终到达this.method2();

使用super不会更改this指向的实例。因此,SubClass.method2()的呼叫转到this,因为SubClass的类型为this

当您想象Java将public class SuperClass { public void method1(SuperClass this) { System.out.println("superclass method1"); this.method2(this); // <--- this == mSubClass } public void method2(SuperClass this) { System.out.println("superclass method2"); } } public class SubClass extends SuperClass { @Override public void method1(SubClass this) { System.out.println("subclass method1"); super.method1(this); } @Override public void method2(SubClass this) { System.out.println("subclass method2"); } } public class Demo { public static void main(String[] args) { SubClass mSubClass = new SubClass(); mSubClass.method1(mSubClass); } } 作为隐藏的第一个参数传递时,可能会更容易理解:

this

如果您按照调用堆栈,您可以看到main()永远不会更改,它始终是在{{1}}中创建的实例。

答案 1 :(得分:12)

您只能在重写方法(或覆盖类的其他方法)中访问重写方法。

所以:要么覆盖method2(),要么在被覆盖的版本中调用super.method2()

答案 2 :(得分:8)

您使用的this关键字实际上是指“您正在使用的对象的当前正在运行的实例”,也就是说,您正在超类上调用this.method2();,即,它将在你正在使用的对象上调用method2(),这是SubClass。

答案 3 :(得分:3)

我是这样想的

+----------------+
|     super      |
+----------------+ <-----------------+
| +------------+ |                   |
| |    this    | | <-+               |
| +------------+ |   |               |
| | @method1() | |   |               |
| | @method2() | |   |               |
| +------------+ |   |               |
|    method4()   |   |               |
|    method5()   |   |               |
+----------------+   |               |
    We instantiate that class, not that one!

让我将该子类稍微向左移动,以揭示下面的内容...... (伙计,我喜欢ASCII图形)

We are here
        |
       /  +----------------+
      |   |     super      |
      v   +----------------+
+------------+             |
|    this    |             |
+------------+             |
| @method1() | method1()   |
| @method2() | method2()   |
+------------+ method3()   |
          |    method4()   |
          |    method5()   |
          +----------------+

Then we call the method
over here...
      |               +----------------+
 _____/               |     super      |
/                     +----------------+
|   +------------+    |    bar()       |
|   |    this    |    |    foo()       |
|   +------------+    |    method0()   |
+-> | @method1() |--->|    method1()   | <------------------------------+
    | @method2() | ^  |    method2()   |                                |
    +------------+ |  |    method3()   |                                |
                   |  |    method4()   |                                |
                   |  |    method5()   |                                |
                   |  +----------------+                                |
                   \______________________________________              |
                                                          \             |
                                                          |             |
...which calls super, thus calling the super's method1() here, so that that
method (the overidden one) is executed instead[of the overriding one].

Keep in mind that, in the inheritance hierarchy, since the instantiated
class is the sub one, for methods called via super.something() everything
is the same except for one thing (two, actually): "this" means "the only
this we have" (a pointer to the class we have instantiated, the
subclass), even when java syntax allows us to omit "this" (most of the
time); "super", though, is polymorphism-aware and always refers to the
superclass of the class (instantiated or not) that we're actually
executing code from ("this" is about objects [and can't be used in a
static context], super is about classes).

换句话说,引用Java Language Specification

  

表单super.Identifier指的是名为Identifier的字段   当前对象,但将当前对象视为一个实例   当前类的超类。

     

表单T.super.Identifier指的是名为Identifier的字段   与T对应的词汇封闭实例,但随之而来   实例被视为T的超类的实例。

在外行人的术语中,this基本上是一个对象(* **对象;你可以在变量中移动的同一个对象),实例化类的实例,一个普通变量in数据域; super就像是一个指向你想要执行的借用代码块的指针,更像是一个函数调用,而且它与调用它的类相关。

因此,如果你使用来自超类的super,你可以从superduper类[祖父母]执行的代码中获取代码,而如果你使用this(或者如果隐含地使用它)一个超类,它一直指向子类(因为没有人改变它 - 没有人可以)。

答案 4 :(得分:2)

如果您不希望superClass.method1调用subClass.method2,请将method2设为私有,以便无法覆盖它。

这是一个建议:

public class SuperClass {

  public void method1() {
    System.out.println("superclass method1");
    this.internalMethod2();
  }

  public void method2()  {
    // this method can be overridden.  
    // It can still be invoked by a childclass using super
    internalMethod2();
  }

  private void internalMethod2()  {
    // this one cannot.  Call this one if you want to be sure to use
    // this implementation.
    System.out.println("superclass method2");
  }

}

public class SubClass extends SuperClass {

  @Override
  public void method1() {
    System.out.println("subclass method1");
    super.method1();
  }

  @Override
  public void method2() {
    System.out.println("subclass method2");
  }
}

如果它不能以这种方式工作,那么多态性将是不可能的(或者至少不是有用的一半)。

答案 5 :(得分:2)

由于避免使用方法获取覆盖的唯一方法是使用关键字 super ,我认为将method2()从 SuperClass 提升到另一个新的 Base 类,然后从 SuperClass 调用它:

class Base 
{
    public void method2()
    {
        System.out.println("superclass method2");
    }
}

class SuperClass extends Base
{
    public void method1()
    {
        System.out.println("superclass method1");
        super.method2();
    }
}

class SubClass extends SuperClass
{
    @Override
    public void method1()
    {
        System.out.println("subclass method1");
        super.method1();
    }

    @Override
    public void method2()
    {
        System.out.println("subclass method2");
    }
}

public class Demo 
{
    public static void main(String[] args) 
    {
        SubClass mSubClass = new SubClass();
        mSubClass.method1();
    }
}

输出:

subclass method1
superclass method1
superclass method2

答案 6 :(得分:2)

    class SuperClass
{
    public void method1()
    {
        System.out.println("superclass method1");
        SuperClass se=new SuperClass();
        se.method2();

    }

    public void method2()
    {
        System.out.println("superclass method2");
    }

}



 class SubClass extends SuperClass
{
    @Override

    public void method1()
    {
        System.out.println("subclass method1");
        super.method1();



    }

    @Override

    public void method2()
    {

        System.out.println("subclass method2");
    }
}



public class Demo 
{
    public static void main(String[] args) 
    {
        SubClass mSubClass = new SubClass();
        mSubClass.method1();
    }
}

输出:

子类方法1
超类方法1 超类方法2

答案 7 :(得分:2)

this总是指当前正在执行的对象。

进一步说明这一点是一个简单的草图:

+----------------+
|  Subclass      |
|----------------|
|  @method1()    |
|  @method2()    |
|                |
| +------------+ |
| | Superclass | |
| |------------| |
| | method1()  | |
| | method2()  | |
| +------------+ |
+----------------+

如果你有一个外框的实例,一个Subclass对象,无论你碰巧在框内冒险,甚至进入Superclass'区域',它仍然是外部的实例框。

更重要的是,在这个程序中只有一个对象是从三个类中创建的,所以this只能引用一个东西,它是:

enter image description here

Netbeans'Heap Walker'所示。

答案 8 :(得分:1)

我不相信你可以直接做到。一种解决方法是在超类中使用method2的私有内部实现,并调用它。例如:

public class SuperClass
{
    public void method1()
    {
        System.out.println("superclass method1");
        this.internalMethod2();
    }

    public void method2()
    {
        this.internalMethod2(); 
    }
    private void internalMethod2()
    {
        System.out.println("superclass method2");
    }

}

答案 9 :(得分:1)

“this”关键字指的是当前的类引用。这意味着,当在方法中使用它时,'current'类仍然是SubClass,因此,解释了答案。

答案 10 :(得分:1)

总而言之,这指向当前对象,java中的方法调用本质上是多态的。因此,执行的方法选择完全取决于此指向的对象。因此,从父类调用方法method2()会调用子类的method2(),因为这指向子类的对象。无论使用哪种类别,这种定义都不会发生变化。

PS。与方法不同,类的成员变量不是多态的。

答案 11 :(得分:0)

在我研究类似案例的过程中,我最终通过检查子类方法中的堆栈跟踪来找出调用的来源。可能有更聪明的方法,但它对我有用,而且是一种动态的方法。

public void method2(){
        Exception ex=new Exception();
        StackTraceElement[] ste=ex.getStackTrace();
        if(ste[1].getClassName().equals(this.getClass().getSuperclass().getName())){
            super.method2();
        }
        else{
            //subclass method2 code
        }
}

我认为解决案​​件的问题是合理的。当然有方法可以用不同的方法名称甚至不同的参数类型来解决问题,就像线程中已经提到过的那样,但在我的情况下,我不喜欢用不同的方法名称来混淆。

答案 12 :(得分:0)

进一步扩展了提出的问题的输出,这将使您对访问说明符和覆盖行为有更多的了解。

            package overridefunction;
            public class SuperClass 
                {
                public void method1()
                {
                    System.out.println("superclass method1");
                    this.method2();
                    this.method3();
                    this.method4();
                    this.method5();
                }
                public void method2()
                {
                    System.out.println("superclass method2");
                }
                private void method3()
                {
                    System.out.println("superclass method3");
                }
                protected void method4()
                {
                    System.out.println("superclass method4");
                }
                void method5()
                {
                    System.out.println("superclass method5");
                }
            }

            package overridefunction;
            public class SubClass extends SuperClass
            {
                @Override
                public void method1()
                {
                    System.out.println("subclass method1");
                    super.method1();
                }
                @Override
                public void method2()
                {
                    System.out.println("subclass method2");
                }
                // @Override
                private void method3()
                {
                    System.out.println("subclass method3");
                }
                @Override
                protected void method4()
                {
                    System.out.println("subclass method4");
                }
                @Override
                void method5()
                {
                    System.out.println("subclass method5");
                }
            }

            package overridefunction;
            public class Demo 
            {
                public static void main(String[] args) 
                {
                    SubClass mSubClass = new SubClass();
                    mSubClass.method1();
                }
            }

            subclass method1
            superclass method1
            subclass method2
            superclass method3
            subclass method4
            subclass method5