Java: вызов супер-метода, который вызывает переопределенный метод - PullRequest
82 голосов
/ 04 января 2011
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
метод суперкласса
метод суперкласса2

фактическая выработка:

метод подкласса1
метод суперкласса1
метод подкласса2

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

Ответы [ 12 ]

68 голосов
/ 04 января 2011

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

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

Вы даже не можете достичь желаемого эффекта с помощью Reflection (см. документация 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.

Может быть, легче понять, когда вы представляете, что 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().

12 голосов
/ 04 января 2011

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

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

8 голосов
/ 04 января 2011

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

4 голосов
/ 12 августа 2016

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

+----------------+
|     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 из лексически включающий экземпляр, соответствующий T, но с этим экземпляр рассматривается как экземпляр суперкласса T.

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

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

2 голосов
/ 25 июня 2016

this всегда ссылается на выполняемый в данный момент объект.

Для дальнейшей иллюстрации приведем простой эскиз:

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

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

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

enter image description here

, как показано на Netbeans «Кучи Уокер».

2 голосов
/ 17 сентября 2014
    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
метод суперкласса
метод суперкласса2

2 голосов
/ 14 сентября 2014

Поскольку единственный способ избежать переопределения метода - использовать ключевое слово super , я подумал о том, чтобы переместить метод method2 () с SuperClass на другой новый Base class и затем вызовите его из 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
2 голосов
/ 05 января 2011

Если вы не хотите, чтобы superClass.method1 вызывал subClass.method2, сделайте метод2 закрытым, чтобы его нельзя было переопределить.

Вот предложение:

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

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

1 голос
/ 27 сентября 2016

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

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

1 голос
/ 20 июня 2013

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

...