192 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			192 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| 
								 | 
							
								---
							 | 
						|||
| 
								 | 
							
								title: Inheritance
							 | 
						|||
| 
								 | 
							
								localeTitle: наследование
							 | 
						|||
| 
								 | 
							
								---
							 | 
						|||
| 
								 | 
							
								# наследование
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Наследование Java относится к способности класса Java `inherit` свойства другого класса. Подумайте об этом, как о ребенке, наследующем свойства от своих родителей, концепция очень похожа на это. В Java lingo он также называется _расширением_ класса. Некоторые простые вещи, которые нужно запомнить:
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								*   Класс, который распространяется или наследуется, называется **подклассом**
							 | 
						|||
| 
								 | 
							
								*   Класс, который расширяется или унаследован, называется **суперклассом**
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Таким образом, наследование дает Java отличную возможность _повторного использования_ кода или обмена кодами между классами!
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Давайте опишем его с классическим примером класса `Vehicle` класса `Car` :
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								```java
							 | 
						|||
| 
								 | 
							
								public class Vehicle { 
							 | 
						|||
| 
								 | 
							
								    public void start() { 
							 | 
						|||
| 
								 | 
							
								        // starting the engine 
							 | 
						|||
| 
								 | 
							
								    } 
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								    public void stop() { 
							 | 
						|||
| 
								 | 
							
								        // stopping the engine 
							 | 
						|||
| 
								 | 
							
								    } 
							 | 
						|||
| 
								 | 
							
								 } 
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								 public class Car extends Vehicle { 
							 | 
						|||
| 
								 | 
							
								    int numberOfSeats = 4; 
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								    public int getNumberOfSeats() { 
							 | 
						|||
| 
								 | 
							
								        return numberOfSeats; 
							 | 
						|||
| 
								 | 
							
								    } 
							 | 
						|||
| 
								 | 
							
								 } 
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Здесь мы можем увидеть класс `Car` наследующий свойства класса `Vehicle` . Таким образом, нам не нужно писать один и тот же код для методов `start()` и `stop()` для `Car` , так как эти свойства доступны из его родительского или суперкласса. Поэтому объекты, созданные из класса `Car` , _также_ будут обладать этими свойствами!
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								```java
							 | 
						|||
| 
								 | 
							
								Car tesla = new Car(); 
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								 tesla.start(); 
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								 tesla.stop(); 
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								 [Код запуска](https://repl.it/CJXz/0)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Но имеет ли родительский класс методы для ребенка? Нет, нет.
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Следовательно, всякий раз, когда вам нужно разделить некоторые общие части кода между несколькими классами, всегда полезно иметь родительский класс, а затем распространять этот класс всякий раз, когда это необходимо! Сокращает количество строк кода, делает код модульным и упрощает тестирование.
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								## Что можно унаследовать?
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								*   Все `protected` и `public` поля и методы от родительских
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								## Что нельзя унаследовать?
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								*   `private` поля и методы
							 | 
						|||
| 
								 | 
							
								*   Конструкторы. Хотя конструктор подкласса _должен_ вызывать конструктор суперкласса, если он определен (подробнее об этом позже!)
							 | 
						|||
| 
								 | 
							
								*   Несколько классов. Java поддерживает только **одно наследование** , то есть вы можете наследовать только один класс за раз.
							 | 
						|||
| 
								 | 
							
								*   Поля. Отдельные поля класса не могут быть переопределены подклассом.
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								## Тип литья и ссылки
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								В Java можно ссылаться на подкласс как _экземпляр_ его суперкласса. Он называется _полиморфизмом_ в объектно-ориентированном программировании (ООП), возможностью для объекта принимать различные формы. Например, объект класса `Car` можно называть экземпляром класса `Vehicle` следующим образом:
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								```java
							 | 
						|||
| 
								 | 
							
								Vehicle car = new Car(); 
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Хотя, наоборот, невозможно:
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								```java
							 | 
						|||
| 
								 | 
							
								Car car = new Vehicle(); // ERROR 
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								 [Код запуска](https://repl.it/CJYB/0)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Поскольку вы можете ссылаться на подкласс Java как экземпляр суперкласса, вы можете легко передать экземпляр объекта подкласса в экземпляр суперкласса. Можно включить объект суперкласса в тип подкласса, но _только если объект действительно является экземпляром подкласса_ . Поэтому имейте это в виду:
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								```java
							 | 
						|||
| 
								 | 
							
								Car car = new Car(); 
							 | 
						|||
| 
								 | 
							
								 Vehicle vehicle = car; // upcasting 
							 | 
						|||
| 
								 | 
							
								 Car car2 = (Car)vechile; //downcasting 
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								 Bike bike = new Bike(); // say Bike is also a subclass of Vehicle 
							 | 
						|||
| 
								 | 
							
								 Vehicle v = bike; // upcasting, no problem here. 
							 | 
						|||
| 
								 | 
							
								 Car car3 = (Car)bike; // Compilation Error : as bike is NOT a instance of Car 
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								 [Код запуска](https://repl.it/CJYM/0)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Теперь вы знаете, как делиться кодом через отношения родитель-ребенок. Но что, если вам не нравится реализация определенного метода в дочернем классе и хотите написать для него новый? Что вы делаете тогда?
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								## Переопределите это!
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Java позволяет вам _переопределять_ или переопределять методы, определенные в суперклассе. Например, ваш класс `Car` имеет другую реализацию `start()` чем исходный `Vehicle` , поэтому вы делаете это:
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								```java
							 | 
						|||
| 
								 | 
							
								public class Vehicle { 
							 | 
						|||
| 
								 | 
							
								    public void start() { 
							 | 
						|||
| 
								 | 
							
								      System.out.println("Vehicle start code"); 
							 | 
						|||
| 
								 | 
							
								    } 
							 | 
						|||
| 
								 | 
							
								 } 
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								 public class Car extends Vehicle { 
							 | 
						|||
| 
								 | 
							
								    public void start() { 
							 | 
						|||
| 
								 | 
							
								      System.out.println("Car start code"); 
							 | 
						|||
| 
								 | 
							
								  } 
							 | 
						|||
| 
								 | 
							
								 } 
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								 Car car = new Car(); 
							 | 
						|||
| 
								 | 
							
								 car.start(); // "Car start code" 
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								 [Код запуска](https://repl.it/CJYZ/1)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Таким образом, довольно просто переопределить методы в подклассе. Хотя, есть _улов_ . Только этот метод суперкласса с той _же сигнатурой метода,_ что и метод подкласса, будет переопределен. Это означает, что определение метода подкласса должно иметь одно и то же имя, одинаковое количество и тип параметров и в той же последовательности. Таким образом, `public void start(String key)` не будет отменять `public void start()` .
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								**Примечания** :
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								*   Вы не можете переопределить частные методы суперкласса. (Совершенно очевидно, не так ли?)
							 | 
						|||
| 
								 | 
							
								*   Что делать, если метод суперкласса, который вы переопределяете в подклассе, внезапно исчезает или меняются методы? Это не сработает во время выполнения! Поэтому Java предоставляет вам замечательную аннотацию `@Override` которую вы можете разместить над методом подкласса, который будет предупреждать компилятор об этих инцидентах!
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Аннотации на Java - хорошая практика кодирования, но они не являются необходимостью. Компилятор достаточно умен, чтобы разобраться в себе. В отличие от других языков ООП, аннотации в Java не обязательно изменят метод или добавляют дополнительные функции.
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								## Как назвать методы суперкласса?
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Забавно, что вы спрашиваете об этом! Просто используйте ключевое слово `super` :
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								```java
							 | 
						|||
| 
								 | 
							
								public class Vehicle() { 
							 | 
						|||
| 
								 | 
							
								    public void start() { 
							 | 
						|||
| 
								 | 
							
								      System.out.println("Vehicle start code"); 
							 | 
						|||
| 
								 | 
							
								    } 
							 | 
						|||
| 
								 | 
							
								 } 
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								 public class Car extends Vehicle { 
							 | 
						|||
| 
								 | 
							
								    public void run() { 
							 | 
						|||
| 
								 | 
							
								      super.start(); 
							 | 
						|||
| 
								 | 
							
								  } 
							 | 
						|||
| 
								 | 
							
								 } 
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								 Car car = new Car(); 
							 | 
						|||
| 
								 | 
							
								 car.run(); // "Vehicle start code" 
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								 [Код запуска](https://repl.it/CJY4/0)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								**NB** : Хотя вы можете вызвать родительский метод, используя `super` , вы не можете подойти к иерархии наследования с помощью прикованных `super` вызовов.
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								## Как узнать тип класса?
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Использование ключевого слова `instanceof` . Обладая множеством классов и подклассов, было бы немного смутно знать, какой класс является подклассом, из которых один во время выполнения. Таким образом, мы можем использовать `instanceof` для определения того, является ли объект экземпляром класса, экземпляром подкласса или экземпляром интерфейса.
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								```java
							 | 
						|||
| 
								 | 
							
								Car car = new Car(); 
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								 boolean flag = car instanceof Vehicle; // true in this case! 
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								## Конструкторы и наследование
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Как упоминалось ранее, конструкторы не могут быть непосредственно унаследованы подклассом. Хотя подкласс _требуется_ для вызова конструктора своего родителя в качестве [первой операции](http://stackoverflow.com/questions/1168345/why-does-this-and-super-have-to-be-the-first-statement-in-a-constructor) в своем собственном конструкторе. Как? Вы догадались, используя `super` :
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								```java
							 | 
						|||
| 
								 | 
							
								public class Vehicle { 
							 | 
						|||
| 
								 | 
							
								    public Vehicle() { 
							 | 
						|||
| 
								 | 
							
								        // constructor 
							 | 
						|||
| 
								 | 
							
								    } 
							 | 
						|||
| 
								 | 
							
								    public void start() { 
							 | 
						|||
| 
								 | 
							
								      System.out.println("Vehicle start code"); 
							 | 
						|||
| 
								 | 
							
								    } 
							 | 
						|||
| 
								 | 
							
								 } 
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								 public class Car extends Vehicle { 
							 | 
						|||
| 
								 | 
							
								    public Car() { 
							 | 
						|||
| 
								 | 
							
								      super(); 
							 | 
						|||
| 
								 | 
							
								    } 
							 | 
						|||
| 
								 | 
							
								    public void run() { 
							 | 
						|||
| 
								 | 
							
								      super.start(); 
							 | 
						|||
| 
								 | 
							
								  } 
							 | 
						|||
| 
								 | 
							
								 } 
							 | 
						|||
| 
								 | 
							
								```
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								 [Код запуска](https://repl.it/CJY8/0)
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Помните, что если суперкласс не имеет определенных конструкторов, вам не нужно его явно указывать в подклассе. Java обрабатывает это внутренне для вас! Вызов в `super` выполняется в случае, когда суперкласс должен вызываться с любым другим конструктором, отличным от _конструктора_ по _умолчанию_ .
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Если никакие другие конструкторы не определены, то Java вызывает конструктор суперкласса по умолчанию ( _даже если он явно не определен_ ).
							 | 
						|||
| 
								 | 
							
								
							 | 
						|||
| 
								 | 
							
								Поздравляю, теперь вы все знаете о Наследии! Подробнее о расширенных способах наследования вещей в абстрактных классах и [интерфейсах](//forum.freecodecamp.com/t/java-docs-interfaces) !
							 |