2015-08-22 3 views
4

Обычная практика всегда использовать супер для вызова методов вне суперкласса, даже если я НЕ переопределяю метод?Использование метода super.method(), когда вы не переопределяете метод?

Предположим

public class Parent{ 
    public void method() { 

    } 
} 

So

public class Child extends Parent { 
    public void someMethod() { 
     super.method(); 
    } 
} 

или

public class Child extends Parent { 
    public void someMethod() { 
     method(); 
    } 
} 

Спасибо за ваш вклад.

ответ

2

Ведущий вопрос легко ответить:

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

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

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

Теперь к опасно запутанным часть явного вызова супер:

public class CallSuper { 

    static class Parent { 
     public void foo() { 
      System.out.println("Parent.foo"); 
     } 

     /** 
     * Calls foo to do the main work 
     */ 
     public void bar() { 
      System.out.print 
      foo(); 
     } 
    } 

    static class Child extends Parent { 
     public void foo() { 
      System.out.println("Child.foo"); 
     } 

     public void bar() { 
      super.foo(); 
     } 
    } 

    static class GrandChild extends Child { 
     public void foo() { 
      System.out.println("GrandChild.foo"); 
     } 
    } 

    public static void main(String[] args) { 
     Parent p = new Parent(); 
     Parent c = new Child(); 
     Parent g = new GrandChild(); 

     System.out.print("Parent.foo() = "); 
     p.foo(); 
     System.out.print("Child.foo() = "); 
     c.foo(); 
     System.out.print("GrandChild.foo() = "); 
     g.foo(); 

     System.out.print("Parent.bar() = "); 
     p.bar(); 
     System.out.print("Child.bar() = "); 
     c.bar(); 
     System.out.print("GrandChild.bar() = "); 
     g.bar(); 
    } 
} 

Если запустить этот пример выведет (добавлены номера строк для ясности):

1 Parent.foo() = Parent.foo 
2 Child.foo() = Child.foo 
3 GrandChild.foo() = GrandChild.foo 
4 Parent.bar() = Parent.foo 
5 Child.bar() = Parent.foo 
6 GrandChild.bar() = Parent.foo 

Первые четыре строки не удивительны, мы получаем то, что foo реализует на каждом уровне, соответственно, бар Parents, вызывающий его foo. Он начинает становиться нечетным в строке 5. Ребенок обходит свое собственное переопределение foo, вызывая super.bar(), поэтому, когда его foo() специализирован, bar() не является. В строке 6 вы видите, что GrandChild наследует эту странность от Child, поэтому несколько невинно выглядящий super.foo() в детском баре теперь нарушил ожидания GrandChild, что его переопределение foo() также эффективно для всех bar().

Теперь, если вы представляете себе foo() и bar() на самом деле что-то делает полезно и что они имеют значимые отношения друг с другом, например. вы вынуждены верить (либо документацией Родителя, либо просто здравым смыслом), что переопределение foo() также изменит поведение bar(). Ребенок «супер» нарушает это.

Как правило, если вы видите вызов «super.x()» где-то вне фактического переопределить «x()», то, вероятно, это произойдет.

3

Вызов super сообщает JVM явно Посмотрите на версию родительского класса метода. Продолжая ваш пример, если вы просто позвоните

method() 

Виртуальная машина Java сначала будет искать в классе вызывающему для метода (в данном случае Child), прежде чем смотреть на родительский класс. С другой стороны, если вы звоните

super.method() 

то JVM будет явно посмотреть на родительский класс для реализации, даже если Child имеет метод, называемый method().

Объединяя эти две идеи, вы должны, вероятно, всегда использовать super, когда вы намереваетесь вызвать метод родительского класса. Если ваш класс не переопределяет метод, то вы можете позвонить без super. Но даже это становится проблематичным, если кто-то должен был реорганизовать ваш класс позже и переопределить метод.

+0

Это правда, но, похоже, это не относится к случаю, который задал ОП. В этом примере предполагается, что ОП задает вопрос о вызове метода super из дочернего метода. Этот ответ касается обхода реализации ребенка от вызова кода. – Brick

+2

@ Брат, ты не понимаешь моего ответа. –

+1

Следует отметить, что если вы не собираетесь переопределять какой-либо метод (и управлять суперклассом), гораздо лучше объявить его «final». В любом случае не используйте 'super' только для повышения производительности. Если JVM видит, что метод не переопределен, вызов 'method()' в горячем коде станет таким же быстрым, как 'super.method()'. –

2

Обычная практика всегда использовать супер для вызова методов из суперкласса? Или это избыточный код?

Это не избыточно. У него есть специальная цель вызвать метод родительского класса (, хотя вы ovveriden). Когда вы не хотите, не вызывайте. Если текущий класс не является дочерним для этого класса, super не будет работать для вас.

Кроме того, если вы звоните перекрытый supermethod, это использовать supermethod или самый низкий метод? (В дереве наследования)

Переопределенный метод. Так как вы переопределили его в своем классе.

+0

Спасибо за ваш ответ, но я до сих пор не знаю, следует ли использовать супер, если я НЕ переопределяю метод. – SJ19

+0

Небо, это действительно зависит. Если вы действительно хотите что-то сделать с помощью метода суперкласса. Я настоятельно рекомендую официальные документы: https://docs.oracle.com/javase/tutorial/java/IandI/super.html. прочитайте. –

+0

Вы должны использовать 'super'. Представьте, что вы или кто-то еще позже решите переопределить 'method()'. Тогда ваш код может сломаться, если вы предполагаете, что вы все еще вызываете метод родительского класса (которого вы не знаете). –

0

Вызов метода супер или полностью зависит от того, что вы пытаетесь сделать. Если вы хотите сделать то, что сделал родительский класс, плюс какое-то дополнительное действие, то вызов метода super хорош. Если вы выполняете что-то совершенно другое в реализации ребенка, тогда не вызывайте супер метод. Когда вы вызываете супер метод в иерархии классов, он вызывает ближайший к вашему дочернему классу в иерархии.

В некоторых случаях у вас может быть API, где вызов метода super является частью общего контракта. Метод clone является таким случаем.

+0

Как правило, использовать супер, даже если вы НЕ переопределяете метод? – SJ19

+0

@Sky Если вы не переопределяете метод, я бы сказал, что это редко можно назвать супер-реализацией. Возможно, это нарушает парадигму проектирования OO. Если нет переопределения, то оно избыточно. Если переопределение будет добавлено позже, вы обойдете это дизайнерское решение, которое, вероятно, будет неправильным в большинстве случаев. (Предположительно, класс ребенка переопределяет причину.) Но в некоторых случаях это может иметь смысл - я бы не хотел предлагать абсолютное правило для такой вещи. – Brick

+0

хорошо, что это очищает – SJ19

0

Если у ребенка есть такой же способ версии с родителем, JVM будет вызывать версию Child вместо версии Parent, если без «супер». Ключевое слово «супер» полезно, если вы хотите, чтобы ребенок всегда вызывал метод версии родителя.

0

Если вы не переопределяете, давайте поговорим о производительности.

Если вы вызываете унаследованный метод в своем классе, скажем, methodInSuperclass(), JVM будет искать этот метод сначала в вашем классе, а затем вверх по иерархии классов, пока он не найдет его.

Использование super.methodInSuperclass() сообщает JVM, чтобы перейти непосредственно к суперклассу, чтобы найти этот метод.

Так, используя ключевое слово super, улучшается производительность несколькими мельницами. Это уродливый код, но он не избыточный, так как это приведет к некоторому повышению производительности.

Рассмотрите возможность использования его, если критическое время.