Java: вызов метода 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();
    }
}

мой ожидаемый выход:

подкласс method1
superclass method1
метода Method2 суперкласса

фактический выход:

подкласс method1
superclass method1
подкласс метода Method2

Я знаю, технически я переопределил публичный метод, но я понял, что, поскольку я вызывал супер, любые вызовы внутри супер останутся в супер, этого не происходит. Любой идеи о том, как я могу это сделать?

12 ответов


ключевое слово super не "палка". Каждый вызов метода обрабатывается индивидуально, поэтому даже если вы получили SuperClass.method1() по телефону super, это не влияет на любой другой вызов метода, который вы можете сделать в будущем.

это означает, что нет прямого способа позвонить SuperClass.method2() С SuperClass.method1() без SubClass.method2() если вы не работаете с фактическим экземпляром SuperClass.

вы даже не можете достичь желаемого эффекта с помощью отражения (см. в документация java.lang.reflect.Method.invoke(Object, Object...)).

[EDIT] кажется, все еще есть некоторая путаница. Позвольте мне попробовать другое объяснение.

при вызове foo(), вы на самом деле вызвать this.foo(). Java просто позволяет опустить this. В Примере в вопросе, типа this is SubClass.

поэтому, когда Java выполняет код в SuperClass.method1(), он в конечном счете приезжает в this.method2();

используя super не меняет экземпляр, на который указывает this. Таким образом, вызов идет SubClass.method2() С this типа SubClass.

возможно, это легче понять, когда вы представляете, что Java проходит this как скрытый первый параметр:

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().


вы можете получить доступ только к переопределенным методам в переопределяющих методах (или в других методах переопределяющего класса).

Итак: либо не переопределяйте method2() или позвоните super.method2() внутри переопределенной версии.


вы используете this ключевое слово, которое фактически относится к "текущему экземпляру объекта, который вы используете", то есть вы вызываете this.method2(); на вашем суперклассе, то есть он вызовет method2() на объекте, который вы используете, который является подклассом.


Если вы не хотите суперкласса.method1 для вызова подкласса.метода 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");
  }
}

Если бы это не работало таким образом, полиморфизм был бы невозможен (или, по крайней мере, не так полезен).


поскольку единственный способ избежать переопределения метода-использовать ключевое слово супер, Я думал, чтобы переместить method2 () из суперкласса к другому новому базовый класс, а затем вызовите его из суперкласса:

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

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

выход:

подкласс method1
superclass method1
метода Method2 суперкласса


this всегда относится к текущему исполняемому объекту.

чтобы еще больше проиллюстрировать этот момент, вот простой эскиз:

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

если у вас есть экземпляр внешнего ящика, a Subclass объект, где бы вы ни оказались внутри коробки, даже в Superclass 'area', это все еще экземпляр внешнего окна.

более того, в этой программе есть только один объект, который создается из трех классов, так this может ссылаться только на одно и то же:

enter image description here

как показано в в NetBeans 'Heap Walker'.


Я не верю, что вы можете сделать это прямо. Одним из обходных путей было бы иметь частную внутреннюю реализацию 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");
    }

}

ключевое слово"this" относится к текущей ссылке на класс. Это означает, что когда он используется внутри метода, "текущий" класс по-прежнему является подклассом, и поэтому ответ объясняется.


я думаю об этом так

+----------------+
|     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:

форму super.Identifier относится к полю с именем Identifier из текущий объект, но с текущим объектом, рассматриваемым как экземпляр суперкласс текущего класса.

форму T.super.Identifier относится к полю с именем Identifier of лексически заключительный экземпляр, соответствующий T, но с этим экземпляр рассматривается как экземпляр суперкласса T.

в терминах непрофессионала,this в основном объект (***объект; тот же самый объект, который вы можете перемещать в переменных), экземпляр экземпляра класса, простая переменная в области данных;super похоже на указатель на заимствованный блок кода, который вы хотите быть выполненным, больше похоже на простой вызов функции, и это относительно класса, где он вызывается.

поэтому, если вы используете super из суперкласса вы получаете код из класса superduper [The grandparent]), а если вы используете this (или если он используется неявно) из суперкласса он продолжает указывать на подкласс (потому что никто его не изменил - и никто не мог).


подводя итог, это указывает на текущий объект, и вызов метода в java является полиморфным по своей природе. Таким образом, выбор метода для выполнения полностью зависит от объекта, на который он указывает. Поэтому вызов метода method2() из родительского класса вызывает method2 () дочернего класса, поскольку this указывает на объект дочернего класса. Определение этого не изменяется, независимо от того, какой класс он используется.

PS. в отличие от методов, переменные-члены класса не являются полиморфный.


во время моего исследования для аналогичного случая я заканчивал проверкой трассировки стека в методе подкласса, чтобы узнать, откуда поступает вызов. Вероятно, есть более разумные способы сделать это, но это работает для меня, и это динамичный подход.

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
        }
}

Я думаю, что вопрос имеет решение по делу является обоснованным. Конечно, есть способы решить проблему с разными именами методов или даже разными типами параметров, как уже упоминалось в нить, но в моем случае я не хотел бы путать разные имена методов.