Вызов супер метода изнутри анонимного внутреннего класса внутри переопределенного метода

Предположим, что мы имеем класс

class A {

  public void m() {
     System.out.println("A - > m()");
  }
}

и я хочу переопределить метод m() при создании класса без, сделав второй подкласс B для расширения A.

public static void main(String[] args) {
    A a = new A() {

        @Override
        public void m() {
            System.out.println("Override - > m()");
            new Thread(new Runnable() {

                @Override
                public void run() {
                   // I want to be able to call the super method.
                   // This is illegal!
                   A.super.m();
                }
            }).start();
        }
    };
    a.method();
}

В настоящее время мое решение заключается в создании частного метода, который вызывает super.m()

   A a = new A() {

        private void superMethod() {
            super.m();
        }

        @Override
        public void m() {
            System.out.println("Overrided - > m()");
            new Thread(new Runnable() {

                @Override
                public void run() {
                    superMethod();
                }
            }).start();
        }
    };
    a.m();

Я хочу знать, почему я не могу написать A.super.m() и есть ли другой способ выполнить эту задачу.

Ответ 1

Я хочу знать, почему я не умею писать A.super.m()...

Это потому, что A на самом деле не является непосредственно окружающим классом. Прямо входящим классом Runnable является new A() { ... }, который является анонимным подклассом A.

Другими словами, если бы вы имели

class A extends Base {
    new Runnable() { ... }
}

тогда A.super работал бы, но теперь у вас есть

class <Anonymous subclass of A> extends A {
    new Runnable() { ... }
}

что означает, что что-то вроде A.super невозможно, так как для <Anonymous subclass of A>.super.m нет синтаксиса.

... и есть ли другой способ выполнить эту задачу.

То, как вы это решили, разумно, на мой взгляд. Другой способ - создать локальный подкласс A (просто ввести идентификатор для использования в ____.super.m) следующим образом:

public static void main(String[] args) {

    class SubA extends A {
        @Override
        public void m() {
            System.out.println("Override - > m()");
            new Thread(new Runnable() {

                @Override
                public void run() {
                    SubA.super.m();
                 // ^^^^ we now have a name of the directly enclosing class
                }
            }).start();
        }
    }
    A a = new SubA();
    a.m();
}

Ответ 2

Написав A.super.m(), предположим, что A имеет суперкласс с методом m. Но в вашем коде вы не указываете суперкласс, и по умолчанию единственный надкласс, который у вас есть, - Object.
Но класс Object не имеет метода "m", поэтому вы не можете его назвать. Хороший способ сделать что-то подобное - использовать шаблон дизайна, например, декоратор.

Ответ 3

Я не думаю, что был бы более простой способ сделать это, кроме того, как у вас уже есть.

Проблема заключается в том, что анонимный класс A (а не базовый класс A) не может ссылаться внутри класса Runnable. Анонимный класс представляется как нечто вроде package.A$1 при компиляции в собственный файл класса. Например, когда вы вызываете superMethod внутри run потока, выполняется следующий байт-код:

getfield mypackage/Test$1$1/this$1 Lmypackage/Test$1;
invokestatic mypackage/Test$1/access$0(Lmypackage/Test$1;)V

Чтобы ссылаться на свой базовый класс A, ссылка на этот внутренний экземпляр класса отсутствует, поэтому вы вызываете выражение super.m().