Подтвердить что ты не робот

Java: вызов супер метода, который вызывает переопределенный метод

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

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

4b9b3361

Ответ 1

Ключевое слово 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().

Ответ 2

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

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

Ответ 3

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

Ответ 4

Если вы не хотите, чтобы 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");
  }
}

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

Ответ 5

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

    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

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

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

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

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

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

введите описание изображения здесь

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

Ответ 8

Я не верю, что вы можете сделать это напрямую. Одним из способов решения этой проблемы было бы иметь частную внутреннюю реализацию метода2 в суперклассе и называть это. Например:

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

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

Ответ 10

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

+----------------+
|     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 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 из суперкласса, вы получаете код из класса superduper [grandparent]), а если вы используете this (или если он неявно) из суперкласса, он продолжает указывать на подкласс (потому что никто не изменил его - и никто не мог).

Ответ 11

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

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

Ответ 12

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

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

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