Почему внутренний класс может переопределять закрытый финальный метод? - PullRequest
66 голосов
/ 02 марта 2012

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

public class Boom {

    private void touchMe() {
        System.out.println("super::I am not overridable!");
    }

    private class Inner extends Boom {

        private void touchMe() {
            super.touchMe();
            System.out.println("sub::You suck! I overrided you!");
        }
    }

    public static void main(String... args) {
        Boom boom = new Boom();
        Boom.Inner inner = boom.new Inner();
        inner.touchMe();
    }
}

Это скомпилировано и работает.«Я должен сделать touchMe () final», - подумал я и сделал это:

public class Boom {

    private final void touchMe() {
        System.out.println("super::I am not overridable!");
    }

    private class Inner extends Boom {

        private void touchMe() {
            super.touchMe();
            System.out.println("sub::You suck! I overrided you!");
        }
    }

    public static void main(String... args) {
        Boom boom = new Boom();
        Boom.Inner inner = boom.new Inner();
        inner.touchMe();
    }
}

, и это также работает и говорит мне

chicout@chicout-linlap:~$ java Boom
super::I am not overridable!
sub::You suck! I overrided you!

почему?

Ответы [ 5 ]

87 голосов
/ 02 марта 2012

Закрытые методы не могут быть переопределены (закрытые методы не наследуются!) На самом деле, не имеет значения, если вы объявите закрытый метод final или нет.

Два метода, которые вы объявили, Boom.touchMe и Boom.Inner.touchMe - это два полностью отдельных метода , которые просто имеют один и тот же идентификатор. Тот факт, что super.touchMe относится к методу, отличному от touchMe, объясняется тем, что Boom.Inner.touchMe shadows Boom.touchMe (а не потому, что он переопределяет его).

Это можно продемонстрировать несколькими способами:

  • Как вы обнаружили сами, если вы измените методы на общедоступные, компилятор будет жаловаться, потому что вы внезапно пытаетесь переопределить последний метод.

  • Если вы оставите методы закрытыми и добавите аннотацию @Override, компилятор будет жаловаться.

  • Как указывает альпийский, если вы приведете объект Boom.Inner к объекту Boom (((Boom) inner).touchMe()), будет вызван Boom.touchMe (если он действительно был переопределен, приведение не будет иметь значения) .

Смежный вопрос:

8 голосов
/ 02 марта 2012

Я думаю, что тот факт, что на самом деле есть два отдельных метода, хорошо демонстрируется следующим образом:

public static void main(String... args) {
    Boom boom = new Boom();
    Boom.Inner inner = boom.new Inner();
    inner.touchMe();
    System.out.println("And now cast it...");
    ((Boom)(inner)).touchMe();
}

Это теперь печатает:

super::I am not overridable!
sub::You suck! I overrided you!
And now cast it...
super::I am not overridable!

И причина того, что вызов super работает в Inner, заключается в том, что вы ищете метод с именем touchMe в вашем суперклассе (Boom), который действительно существует и видим для Inner как в том же классе.

6 голосов
/ 02 марта 2012

Закрытые методы невидимы для подклассов или любого другого класса, поэтому они могут иметь одинаковые имена, но не перекрывать друг друга.

Попробуйте добавить аннотацию @Override - вы получите ошибку компилятора.

2 голосов
/ 02 марта 2012

Вы можете переопределить метод, потому что это private для каждого класса.

1 голос
/ 02 марта 2012

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

public class Boom {

    private final void touchMe() {
        System.out.println("super [touchMe] ::I am not overridable!");
    }

    public void overrideMe(){
        System.out.println("super [overrideMe]::I am overridable!");
    }

    private class Inner extends Boom {

        private void touchMe() {
            super.touchMe();
            System.out.println("sub [touchMe]::You suck! I overrided you!");
        }

        public void overrideMe(){
            System.out.println("sub [overrideMe] ::I overrided you!");
        }
    }

    public static void main(String... args) {
        Boom boom = new Boom();
        Boom.Inner inner = boom.new Inner();

        inner.touchMe();

        Boom newBoom = inner;
        newBoom.touchMe();
        newBoom.overrideMe();
    }
}



super [touchMe] ::I am not overridable!
sub [touchMe]::You suck! I overrided you!
super [touchMe] ::I am not overridable!
sub [overrideMe] ::I overrided you!
...