Loading AI tools
З Вікіпедії, вільної енциклопедії
Наслідування, успадкування (англ. inheritance) — один з принципів об'єктно-орієнтовного програмування[1], який дає класу можливість використовувати програмний код іншого (базового) класу[2], доповнюючи його своїми власними деталями реалізації. Іншими словами, під час наслідування відбувається отримання нового (похідного) класу, який містить програмний код базового класу з зазначенням власних особливостей використання. Наслідування належить до типу is-a відношень між класами. При успадкуванні створюється спеціалізована версія вже існуючого класу.
В Unified Modeling Language наслідування класів відображується на діаграми класів.
Правильне використання механізму наслідування дає наступні взаємозв'язані переваги:
При використанні наслідування в програмах були помічені наступні недоліки:
В об'єктно-орієнтованому програмуванні, починаючи з Simula 67[3], абстрактні типи данних називаются класами.
Базовий клас (англ. base class) — це клас, який знаходиться на вершині ієрархії наслідування класів і в основі дерева підкласів, тобто не є підкласом і не має успадкувань від інших суперкласів або інтерфейсів. Базовим класом може бути абстрактний клас і інтерфейс. Будь-який не базовий клас є підкласом.
Суперклас (англ. superclass), батьківський клас (англ. parent class), предок або надклас — клас, виконує наслідування в підкласах, тобто клас, від якого наслідуються інші класи. Суперкласом може бути підклас, базовий клас, абстрактний клас і інтерфейс.
Підклас (англ. subclass), похідний клас (англ. derived class), дочірній клас (англ. child class), клас нащадок, клас наслідник або клас-реалізатор — клас, успадкований від суперкласу або інтерфейсу, тобто клас визначений через наслідування від іншого класу або деяких таких класів. Підкласом може бути суперклас.
Інтерфейс (англ. interface) — це структура, що визначає чистий інтерфейс класу, що складається з абстрактних методів. Інтерфейси беруть участь в ієрархії наслідування класів і інтерфейсів.
Базовий інтерфейс (англ. base interface) — це аналог базового класу в ієрархії наслідування інтерфейсів, тобто це інтерфейс, який знаходиться на вершині ієрархії успадкування.
Суперінтерфейс (англ. super interface) або інтерфейс-предок — це аналог суперкласу в ієрархії успадкування, тобто це інтерфейс виконує наслідування підкласів і підінтерфейсів.
Інтерфейс-нащадок, інтерфейс-наслідник або похідний інтерфейс (англ. derived interface) — це аналог підкласу в ієрархії наслідування інтерфейсів, тобто це інтерфейс успадкований від одного або декількох суперінтерфейсів.
Ієрархія успадкування або ієрархія класів — дерево, елементами якого є класи та інтерфейси.
Наслідування є механізмом повторного використання коду (англ. code reuse) і сприяє незалежному розширенню програмного забезпечення через відкриті класи (англ. public classes) та інтерфейси (англ. interfaces). Встановлення відношення наслідування між класами породжує ієрархію класів[en].
«Просте» наслідування, або одинарне успадкування, описує спорідненість між двома класами: один з яких успадковується від іншого. З одного класу може виводитися багато класів, але навіть в цьому випадку подібний вид взаємозвязку залишається «простим» успадкуванням.
Для деяких мов програмування справедлива наступна концепція.
Існують «абстрактні» класи (оголошуються такими довільно або через приписаних їм абстрактних методів); їх можна описувати наявними поля та методи. Створення ж об'єктів (екземплярів) означає конкретизацію, застосовну тільки до неабстрактних класів (в тому числі, до неабстрактних наслідникам абстрактних), — представниками яких, в результаті, будуть створені об'єкти.
При множинному успадкуванні, у класа може бути більше одного предка. В цьому випадку клас успадковує методи всіх предків. Переваги такого підходу в більшій гнучкості.
Множинне наслідування реалізовано в C++. З інших мов, що надають цю можливість, можна відмітити Python і Eiffel. Множинне наслідування підтримується в мові UML.
Множинне успадкування — потенційне джерело помилок, які можуть виникати через наявність однакових імен методів у предків. В мовах, які позиціонуются як спадкоємці C++ (Java, C# та інші), було прийнято рішення відмовитись від множинного наслідування на користь інтерфейсів. Практично завжди можна обійтися без використання даного механізму. Однак, якщо така необхідність все-таки виникла, то для вирішення конфліктів використання успадкованих методів з однаковими іменами можливо, наприклад, застосувати операцію розширення видимості — «::» — для виклику конкретного метода конкретного предка.
Спроба вирішення проблеми наявності однакових імен методів в предках була здійснена у мові Eiffel, в якій при описі нового класу необхідно явно вказати імпортовані члени кожного з успадкованих класів і їх імена у дочірньому класі.
Більшість сучасних об'єктно-орієнтованих мов програмуння (C#, Java, Delphi та інші) підтримують можливість одночасно успадковуватись від класа-предка і реалізовувати методи декількох інтерфейсів одним і тим же класом. Цей механізм дозволяє в багато чому замінити множинне успадкування — методи інтерфейсів необхідно перевизначати явно, що виключає помилки при успадкуванні функціональності однакових методів різних класів-предків.
В ряді мов програмування, усі класи, — явно або неявно, — успадковуються від деякого базового класу. Smalltalk був одним з перших мов, в яких використовувалась ця концепція. До таких мов також відносяться: Objective-C (NSObject
), Perl (UNIVERSAL
), Eiffel (ANY
), Java (java.lang.Object
), C# (System.Object
), Delphi (TObject
), Scala (Any
).
Наслідування в C++[4]:
class A {}; // Базовий клас
class B : public A {}; // Public-успадкування
class C : protected A {}; // Protected-успадкування
class Z : private A {}; // Private-успадкування
В C++ існує три типи успадкування: public, protected, private. Специфікатори доступу членів базового класу змінюються в потомках наступним чином: Якщо клас об'явлений як базовий для іншого класу з специфікатором доступу:
Одним з основних переваг public-наслідування є те, що вказівник на класи-наслідники може бути неявно перетворений у вказівник на базовий клас, тобто для прикладу вище можна написати:
A* a = new B();
Ця цікава можливість відкриває можливість динамічної ідентифікації типу (RTTI).
Для використання механізму наслідування в Delphi необхідно в оголошені класу в дужках class
вказати клас предок: Предок:
TAncestor = class
private
protected
public
//Віртуальна процедура
procedure VirtualProcedure; virtual; abstract;
procedure StaticProcedure;
end;
Наслідник:
TDescendant = class(TAncestor)
private
protected
public
//Перекриття віртуальної процедуры
procedure VirtualProcedure; override;
procedure StaticProcedure;
end;
Абсолютно всі класи в Delphi є нащадками класа TObject
. Якщо клас-предок не вказан, то мається на увазі, що новий клас є прямим нащадком класа TObject
.
Множинне наслідування в Delphi з самого початку не підтримується, однак для тих, кому без цього не обійтись все ж є такі можливості, наприклад, за рахунок використання класів-помічників(англ. Class Helpers).
Python підтримує як одиночне, так і множинне успадкування. При доступі до атрибуту, перегляд похідних класів прохидить в порядку розширення метода (англ. method resolution order, MRO).
class Ancestor1(object): # Предок-1
def m1(self): pass
class Ancestor2(object): # Предок-2
def m1(self): pass
class Descendant(Ancestor1, Ancestor2): # Наслідник
def m2(self): pass
d = Descendant() # Ініціалізація
print d.__class__.__mro__ # Порядок розширення метода:
(<class '__main__.Descendant'>, <class '__main__.Ancestor1'>, <class '__main__.Ancestor2'>, <type 'object'>)
З версії Python 2.2[5] в мові існують «класичні» класи і «нові» класи. Останні є наслідниками object
. «Класичні» класи будуть підтримувати включно до версії 2.6, але видалені з мови в Python 3.0.
Множинне наслідування приміняється в Python для введення в основний клас класів-домішок (англ. mix-in).
Для використання механізма наслідування в PHP необхідно в оголошенні класу після імені оголошеного класу-наслідника вказати слово extends
і ім'я класу-предка:
class Descendant extends Ancestor {
}
У випадку перекриття класом-наслідником методів предка, доступ до методів предка можна отримати з використанням ключового слова parent
:
class A {
function example() {
echo "Викликаний метод A::example().\n";
}
}
class B extends A {
function example() {
echo "Викликаний метод B::example().\n";
parent::example();
}
}
Можна запобігти перекриття класом-наслідником методів предка; для цього необхідно вказати ключове слово final
:
class A {
final function example() {
echo "Викликаний метод A::example().\n";
}
}
class B extends A {
function example() { //викличе помилку
parent::example(); //і ніколи не виконається
}
}
Щоб при успадкуванні звернутись до конструктора батьківського класу, необхідно дочірньому класу в конструкторі вказати parent::__construct();
@interface A : NSObject
- (void) example;
@end
@implementation
- (void) example
{
NSLog(@"Class A");
}
@end
@interface B : A
- (void) example;
@end
@implementation
- (void) example
{
NSLog(@"Class B");
}
@end
В інтерфейсі оголошують методи, які буде видно ззовні класу (public).
Внутрішні методи можна реалізовувати без інтерфейсу. Для оголошення додаткових властивостей, користуються interface-extension у файлі реалізації.
Усі методи в Objective-C — віртуальні.
Приклад наслідування від одного класу і двох інтерфейсів:
public class A { }
public interface I1 { }
public interface I2 { }
public class B extends A implements I1, I2 { }
Директива final
в оголошені класа робить наслідування від нього неможливим.
Приклад успадкування[6] від одного класу і двох інтерфейсів:
public class A { }
public interface I1 { }
public interface I2 { }
public class B : A, I1, I2 { }
Наслідування від типізованих класів можна здійснювати, вказавши фіксований тип, або шляхом переносу змінної типу в успадкований клас:
public class A<T>
{ }
public class B : A<int>
{ }
public class B2<T> : A<T>
{ }
Допустимо також наслідування вкладених класів від класів, які їх містять:
class A // default class A is internal, not public class B can not be public (клас A за замовчуванням є внутрішнім, не публічний клас B не може бути публічним)
{
class B : A { }
}
Директива sealed
в оголошені класа робить наслідування від нього неможливим.
class Parent
def public_method
"Public method"
end
private
def private_method
"Private method"
end
end
class Child < Parent
def public_method
"Redefined public method"
end
def call_private_method
"Ancestor's private method: " + private_method
end
end
Клас Parent
є предком для класу Child
, в якого перевизначений метод public_method
.
child = Child.new
child.public_method #=> "Redefined public method"
child.call_private_method #=> "Ancestor's private method: Private method"
Приватні методи предка можна викликати з нащадків.
class Parent {
constructor(data) {
this.data = data;
}
publicMethod() {
return 'Public Method';
}
}
class Child extends Parent {
getData() {
return `Data: ${this.data}`;
}
publicMethod() {
return 'Redefined public method';
}
}
const test = new Child('test');
test.getData(); // => 'Data: test'
test.publicMethod(); // => 'Redefined public method'
test.data; // => 'test'
Клас Parent
є предком для класу Child
, в якого перевизначений метод publicMethod
.
В JavaScript використовується прототипне успадкування.
В С++ конструктори при успадкуванні викликаються почергово від першого предка до останнього нащадка, а деструктори навпаки — від останнього нащадка до першого предка.
class First
{
public:
First() { cout << ">>First constructor" << endl; }
~First() { cout << ">>First destructor" << endl; }
};
class Second: public First
{
public:
Second() { cout << ">Second constructor" << endl; }
~Second() { cout << ">Second destructor" << endl; }
};
class Third: public Second
{
public:
Third() { cout << "Third constructor" << endl; }
~Third() { cout << "Third destructor" << endl; }
};
// виконання коду
Third *th = new Third();
delete th;
// результат виведення
/*
>>First constructor
>Second constructor
Third constructor
Third destructor
>Second destructor
>>First destructor
*/
Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.