Java 8 наследование
В предыдущих статьях я уже несколько раз упоминал наследование. Настало время написать подробную статью про эту вещь.
В Java класс может наследоваться от другого класса, получая его методы и поля, который в свою очередь может наследоваться от ещё одного класса и т. д. В Java нет множественного наследования классов. Один класс может наследоваться напрямую только от одного другого класса.
Класс, который наследуется от другого класса, называется подклассом (subclass), дочерним классом (child class), потомком или расширенным классом (extended class).
Класс, от которого наследуется дочерний класс, называется родительским классом (parent class), предком, суперклассом (superclass) или базовым классом (base class).
В самой вершине иерархии наследования находится класс Object , от которого наследуются все классы, для которых не указан явно суперкласс. Таким образом все классы (кроме самого Object ) напрямую или через какое-либо количество уровней наследования наследуются от класса Object .
Идея наследования классов состоит в том, что когда вы хотите создать новый класс, например Goblin , и уже существует какой-нибудь класс, который уже реализует часть функциональности, необходимой нашему классу, например Monster , то вы можете указать этот класс в качестве родительского класса, унаследовав таким образом все его члены (поля, вложенные классы и методы экземпляров). Конструкторы не наследуются и не являются членами классов, но можно вызвать конструктор базового класса из конструктора дочернего класса.
Дочерний класс наследует все public и protected члены своего родителя независимо от пакета, в котором расположен родительский класс. Если дочерний и родительский класс находятся в одном пакете, то дочерний класс наследует также package-private члены своего родителя.
- Унаследованные поля можно использовать напрямую, как все другие поля.
- Можно объявить в дочернем классе поле с таким же именем, как и поле в родительском классе, тогда это поле скроет (hide) поле родительского класса (НЕ рекомендуется так делать).
- В дочернем классе можно объявлять поля, которых нет в родительском классе.
- Унаследованные методы можно использовать напрямую.
- Можно объявить метод экземпляров в дочернем классе с точно такой же сигнатурой, что и метод экземпляров в родительском классе, тогда этот метод переопределит (override) метод суперкласса.
- Можно объявить в дочернем классе статический метод с точно такой же сигнатурой, что и статический метод в родительском классе, тогда этот метод скроет (hide) метод родительского класса.
- В дочернем классе можно объявлять новые методы, которых нет в родительском классе.
- В дочернем классе можно объявить конструктор, который будет явно (с помощью ключевого слова super ) или неявно вызывать конструктор базового класса.
Дочерний класс не наследует private члены родительского класса, однако если в родительском классе есть protected , public или package-private (для случая нахождения дочернего и родительского класса в одном пакете) методы для доступа к private полям, то они могут использоваться дочерним классом.
Приведение типов
Посмотрите на создание экземпляра объекта Goblin :
Статические методы наследуются в Java?
Наследование членов тесно связано с их объявленными доступность. Если член суперкласса доступен по простому имени в подклассе (без использования какого-либо дополнительного синтаксиса, такого как super), что член считается унаследованным
В нем также упоминается, что статические методы не наследуются. Но приведенный ниже код идеально подходит:
class A < public static void display() < System.out.println("Inside static method of superclass"); >> class B extends A < public void show() < // This works - accessing display() by its simple name - // meaning it is inherited according to the book. display(); >>
Как я могу напрямую использовать display() в классе B ? Более того, работает B.display() . Обоснование книги применимо только к методам экземпляра?
14 ответов
Все доступные методы наследуются подклассами.
Подкласс наследует все общедоступные и защищенные члены его родителя, независимо от того, в каком пакете находится подкласс. Если подкласс находится в том же пакете, что и его родительский элемент, он также наследует дочерние элементы-члены пакета. Вы можете использовать унаследованные элементы как есть, заменить их, скрыть их или дополнить их новыми членами.
Единственное отличие от унаследованных методов static (class) и унаследованных нестатических (экземпляров) методов заключается в том, что при написании нового статического метода с той же сигнатурой старый статический метод просто скрыт, а не переопределяется.
Из страница о различии между переопределением и скрытием.
Различие между сокрытием и переопределением имеет важные последствия. Версия переопределенного метода, который вызывается, является таковой в подклассе. Версия скрытого метода, который вызывается, зависит от того, вызвана ли она из суперкласса или подкласса
Ну, это часть наследства, но не все. Я бы сказал, что другие важные части наследования — это повторное использование кода и полиморфизм.
@Algorithmist Алгоритмист нет не совсем. Все, что ваш подкласс видит далее в иерархии, это то, что унаследовал ваш класс. Но статические методы, которые наследуются, не могут быть переопределены, только скрыты («повторно объявлены» с той же сигнатурой). Следовательно, вы можете также объявить ваши статические методы как final, это не имеет значения. Какой статический метод будет вызываться, известно во время компиляции. При использовании методов неконечного экземпляра разрешение должно быть отложено до времени выполнения, поскольку они могут быть переопределены.
Ваш последний абзац полностью подорван . «Версия переопределенного метода, который вызывается, является версией в подклассе», это не так: допустим: версия переопределенного метода, который вызывается, определяется только во время выполнения JVM, связанной с тем, какой объект сделал вызов 🙂
Если это то, что действительно говорит книга, это неправильно. [1]
8.4.8 Наследование, переопределение и скрытие
- m является членом прямого суперкласса C.
- m является общедоступным, защищенным или объявленным с доступом к пакету в том же пакете, что и C.
- Никакой метод, объявленный в C, не имеет подписи (подстрока) (§8.4.2) подписи m.
Вы можете почувствовать разницу в следующем коде, который немного изменяет ваш код.
class A < public static void display() < System.out.println("Inside static method of superclass"); >> class B extends A < public void show() < display(); >public static void display() < System.out.println("Inside static method of this class"); >> public class Test < public static void main(String[] args) < B b = new B(); b.display(); A a = new B(); a.display(); >>
Это связано с тем, что статические методы являются методами класса.
A.display() и B.display() вызывают метод своих соответствующих классов.
Это то, что объясняет эта программа, создание экземпляра объекта B и ожидаемого наследования будет невозможно со статическими членами. Попробуйте взять тот же код в вашем eclipse / any ide или скомпилировать с помощью javac и выполнить его
@LucasC.FeijoLucasC.Feijo на самом деле я это работает. По крайней мере, в моей IDE (затмение). Я просто получил предупреждение. Это может быть не очень хороший стиль, хотя . но это другая история.
@LucasC.FeijoLucasC.Feijo вызывать статический метод для экземпляра не рекомендуется. Но это работает так же, как и вызов статического метода для имени класса.
B.display() работает, потому что статическое объявление заставляет метод/член принадлежать классу, а не какой-либо конкретный экземпляр класса (aka Object). Вы можете прочитать об этом здесь.
Еще одна вещь, которую следует отметить, заключается в том, что вы не можете переопределить статический метод, вы можете предложить вашему подклассу статический метод с той же сигнатурой, но его поведение может отличаться от того, что вы ожидаете. Вероятно, это причина, по которой она не считается унаследованной. Вы можете проверить проблемный сценарий и объяснение здесь.
Эта концепция не так проста, как кажется. Мы можем получить доступ к статическим членам без наследования, что является отношением HasA. Мы можем получить доступ к статическим членам, также расширив родительский класс. Это не означает, что это отношение ISA (Наследование). Фактически статические члены принадлежат классу, а static не является модификатором доступа. Пока модификаторы доступа допускают доступ к статическим членам, мы можем использовать их в других классах. Например, если он является общедоступным, он будет доступен внутри того же пакета, а также вне пакета. Для частных мы не можем использовать его нигде. По умолчанию мы можем использовать его только внутри пакета. Но для защиты мы должны расширить суперкласс. Таким образом, статический метод для другого класса не зависит от статики. Это зависит от модификаторов доступа. Поэтому, на мой взгляд, члены Статики могут получить доступ, если разрешают модификаторы доступа. В противном случае мы можем использовать их так, как мы используем отношение Хаса. И имеет отношение не наследование. Опять же, мы не можем переопределить статический метод. Если мы можем использовать другой метод, но не можем его переопределить, то это отношение HasA. Если мы не сможем переопределить их, это не будет наследованием. Так что автор был на 100% прав.
Расширение родительского класса является отношением «есть». Если доступ частный, вы можете использовать его из класса. ‘protected’ включает производные классы и классы в текущем пакете. Слишком много ошибок здесь.
Статические методы в Java наследуются, но не могут быть переопределены. Если вы объявляете тот же метод в подклассе, вы скрываете метод суперкласса вместо его переопределения. Статические методы не являются полиморфными. Во время компиляции статический метод будет статически связан.
public class Writer < public static void write() < System.out.println("Writing"); >> public class Author extends Writer < public static void write() < System.out.println("Writing book"); >> public class Programmer extends Writer < public static void write() < System.out.println("Writing code"); >public static void main(String[] args) < Writer w = new Programmer(); w.write(); Writer secondWriter = new Author(); secondWriter.write(); Writer thirdWriter = null; thirdWriter.write(); Author firstAuthor = new Author(); firstAuthor.write(); >>
Writing Writing Writing Writing book
Многие из них высказали свой ответ словами. Это расширенное объяснение в кодах:
public class A < public static void test() < System.out.println("A"); >public static void test2() < System.out.println("Test"); >> public class B extends A < public static void test() < System.out.println("B"); >> // Called statically A.test(); B.test(); System.out.println(); // Called statically, testing static inheritance A.test2(); B.test2(); System.out.println(); // Called via instance object A a = new A(); B b = new B(); a.test(); b.test(); System.out.println(); // Testing inheritance via instance call a.test2(); b.test2(); System.out.println(); // Testing whether calling static method via instance object is dependent on compile or runtime type ((A) b).hi(); System.out.println(); // Testing whether null instance works A nullObj = null; nullObj.hi();
A B Test Test A B Test Test A A
- Когда мы ставим статический статический метод через., Он будет искать статическую информацию, определенную в этом классе, или ближайший к ней класс цепочки наследования. Это доказывает, что статические методы наследуются.
- Когда статический метод вызывается из экземпляра, он вызывает статический метод, определенный в типе времени компиляции.
- Статический метод можно вызвать из null экземпляра. Я предполагаю, что компилятор будет использовать тип переменной для поиска класса во время компиляции и перевести его на соответствующий вызов статического метода.
Простой код не является объяснением, это демонстрация. Ответ на этот вопрос должен ссылаться на нормативные ссылки, а не просто демонстрировать поведение неустановленной реализации.
Вы можете переопределить статические методы, но если вы попытаетесь использовать полиморфизм, то они работают в соответствии с классом (в отличие от того, что мы обычно ожидаем).
public class A < public static void display()< System.out.println("in static method of A"); >> public class B extends A < void show()< display(); >public static void display() < System.out.println("in static method of B"); >> public class Test < public static void main(String[] args)< B obj =new B(); obj.show(); A a_obj=new B(); a_obj.display(); >>
В первом случае o/p является «в статическом методе B» # успешным переопределением Во втором случае o/p является «в статическом методе A» # Статический метод — не будет рассматривать полиморфизм