Dědičnost (angl. inheritance ) - koncept objektově orientovaného programování , podle kterého může abstraktní datový typ zdědit data a funkčnost nějakého existujícího typu, což usnadňuje opětovné použití softwarových komponent .
V objektově orientovaném programování se od Simuly 67 abstraktní datové typy nazývají třídy .
Superclass ( eng. super class ), parent class ( eng. parent class ), předek, parent nebo superclass - třída, která produkuje dědičnost v podtřídách, tedy třída, ze které dědí ostatní třídy. Nadtřídou může být podtřída, základní třída, abstraktní třída a rozhraní.
Podtřída ( eng. subclass ), odvozená třída ( eng. odvozená třída ), podřízená třída ( eng. child class ), podřízená třída, následnická třída nebo implementační třída - třída zděděná z nadtřídy nebo rozhraní, tj. třída definovaná prostřednictvím dědičnosti z další třída nebo několik takových tříd. Podtřída může být nadtřídou.
Základní třída je třída , která je na vrcholu hierarchie dědičnosti tříd a na konci stromu podtříd, to znamená, že není podtřídou a nedědí z jiných nadtříd nebo rozhraní. Základní třídou může být abstraktní třída a rozhraní. Jakákoli nezákladová třída je podtřídou.
Rozhraní je struktura , která definuje čisté rozhraní třídy sestávající z abstraktních metod. Rozhraní se účastní hierarchie dědičnosti tříd a rozhraní.
Superinterface ( anglicky super interface ) nebo rozhraní předka je analogií nadtřídy v hierarchii dědičnosti, to znamená, že je to rozhraní, které dědí v podtřídách a podrozhraních.
Rozhraní potomka, odvozené rozhraní nebo odvozené rozhraní je analogem podtřídy v hierarchii dědičnosti rozhraní, tj. je to rozhraní zděděné z jednoho nebo více superrozhraní.
Základní rozhraní je ekvivalentem základní třídy v hierarchii dědičnosti rozhraní, tj. jedná se o rozhraní na vrcholu hierarchie dědičnosti.
Hierarchie dědičnosti nebo hierarchie tříd je strom, jehož prvky jsou třídy a rozhraní.
Dědičnost je mechanismus pro opětovné použití kódu (anglicky code reuse ) a přispívá k nezávislému rozšiřování softwaru prostřednictvím otevřených tříd (anglické veřejné třídy) a rozhraní (anglická rozhraní). Nastavením vztahu dědičnosti mezi třídami se generuje hierarchie tříd.
Dědičnost je často identifikována s polymorfismem podtypů :
Navzdory výše uvedené poznámce je dědičnost široce používaným mechanismem pro vytvoření vztahu is -a. Některé programovací jazyky souhlasí s dědičností a polymorfismem podtypů (většinou staticky typované jazyky jako C++ , C# , Java a Scala ), zatímco jiné sdílejí výše uvedené koncepty.
Dědičnost – dokonce ani v programovacích jazycích, které podporují použití dědičnosti jako mechanismu pro polymorfismus podtypů – nezaručuje polymorfismus chování podtypů; viz: „Princip substituce“ od Barbary Liskov .
"Jednoduchá" dědičnost, někdy nazývaná jednoduchá dědičnost, popisuje vztah mezi dvěma třídami, z nichž jedna dědí druhou. Mnoho tříd může pocházet z jedné třídy, ale i tak tento druh vztahu zůstává "jednoduchým" dědičností.
Abstraktní třídy a tvorba objektůPro některé programovací jazyky platí následující koncept.
Existují „abstraktní“ třídy (deklarované jako takové svévolně nebo kvůli abstraktním metodám , které jim byly přiřazeny ); mohou být popsány jako mající pole a metody . Vytváření objektů (instancí) znamená konkretizaci , použitelnou pouze pro neabstraktní třídy (včetně neabstraktních potomků abstraktních), jejichž představiteli budou ve výsledku vytvořené objekty.
Příklad: Základní třída „Zaměstnanec univerzity “, ze které se dědí třídy „ Doktorand “ a „ Profesor “, nechť je abstraktní. Běžná pole a funkce tříd (například pole „Rok narození“) lze popsat v základní třídě. A program vytvoří objekty pouze odvozených tříd: "Postgraduální student" a "Profesor"; obvykle nemá smysl vytvářet objekty základních tříd.
Při vícenásobné dědičnosti může mít třída více než jednoho rodiče. V tomto případě třída zdědí metody všech předků. Výhodou tohoto přístupu je větší flexibilita.
Vícenásobná dědičnost je implementována v C++ . Mezi další jazyky, které tuto funkci poskytují, patří Python a Eiffel . V UML je podporována vícenásobná dědičnost .
Vícenásobná dědičnost je potenciálním zdrojem chyb, které mohou vzniknout v důsledku stejných názvů metod v předcích. V jazycích, které jsou umístěny jako nástupci C++ ( Java , C# a další), bylo rozhodnuto opustit vícenásobnou dědičnost ve prospěch rozhraní . Bez použití tohoto mechanismu se téměř vždy obejdete. Pokud však přesto taková potřeba vyvstala, pak pro vyřešení konfliktů při použití zděděných metod se stejnými názvy je možné například použít operaci rozšíření viditelnosti - "::" - zavolat konkrétní metodu a konkrétního rodiče.
Pokus o vyřešení problému se stejnými názvy metod u předků byl učiněn v jazyce Eiffel , ve kterém je při popisu nové třídy nutné explicitně uvést importované členy každé z zděděných tříd a jejich pojmenování v dětská třída.
Většina moderních objektově orientovaných programovacích jazyků ( C# , Java , Delphi a další) podporuje schopnost současně dědit od třídy předků a implementovat metody několika rozhraní stejnou třídou. Tento mechanismus umožňuje do značné míry nahradit vícenásobnou dědičnost – metody rozhraní musí být explicitně předefinovány, což eliminuje chyby při dědění funkčnosti stejných metod různých tříd předků.
V řadě programovacích jazyků všechny třídy, ať už explicitně nebo implicitně, dědí z nějaké základní třídy. Smalltalk byl jedním z prvních jazyků, které tento koncept použily. Mezi tyto jazyky také patří: Objective-C (třída NSObject), Perl ( UNIVERSAL), Eiffel ( ANY), Java ( java.lang.Object), C# ( System.Object), Delphi ( TObject), Scala ( Any).
Dědičnost v C++ :
třídaA { }; // Základní třída třída B : public A {}; // Veřejná dědičnost třída C : protected A {}; // Třída chráněné dědičnosti Z : private A {}; // Soukromé dědictvíV C++ existují tři typy dědičnosti : veřejné , chráněné , soukromé . Specifikátory přístupu členů základní třídy se v potomcích mění takto:
Jednou z hlavních výhod veřejné dědičnosti je, že ukazatel na odvozené třídy lze implicitně převést na ukazatel na základní třídu, takže pro výše uvedený příklad můžete napsat:
A * a = novýB ( );Tato zajímavá funkce otevírá možnost dynamické identifikace typu (RTTI).
Chcete-li použít mechanismus dědičnosti v Delphiclass , musíte zadat třídu předka v deklaraci třídy v závorkách :
Předek:
TAncestor = class private protected public // Procedura virtuální procedury VirtualProcedure ; virtuální ; abstraktní ; procedura StaticProcedure ; konec ;Dědic:
TDescendant = class ( TAncestor ) private protected public // Virtual procedure override procedure VirtualProcedure ; přepsat ; procedura StaticProcedure ; konec ;Absolutně všechny třídy v Delphi jsou potomky třídy TObject. Pokud není uvedena třída předka, předpokládá se, že nová třída je přímým potomkem třídy TObject.
Vícenásobná dědičnost v Delphi není zpočátku v zásadě podporována, ale pro ty, kteří se bez ní neobejdou, stále existují takové příležitosti, například pomocí pomocných tříd (Сlass Helpers).
Python podporuje jednoduché i vícenásobné dědění. Při přístupu k atributu dochází k prohlížení odvozených tříd v pořadí podle pořadí rozlišení metod (MRO ) [1] .
class Předek1 ( objekt ): # Předek-1 def m1 ( self ): předat třídu Předek2 ( objekt ): # Předek-2 def m1 ( self ): předat třídu Potomek ( Předek1 , Předek2 ): # Potomek def m2 ( self ): složit d = Potomek () # Tisk instance d . __třída__ . __mro__ # Pořadí rozlišení metody: ( < class ' __main__ . Potomek '>, <class ' __main__ . Předek1 '>, <třída ' __main__ . Předek2 '>, <type ' objekt '>)Od Pythonu 2.2 koexistují v jazyce „klasické“ třídy a „nové“ třídy. Ti poslední jsou dědici object. Třídy „Classic“ budou podporovány až do verze 2.6, ale budou odstraněny z jazyka v Pythonu 3.0.
Vícenásobná dědičnost se v Pythonu používá zejména k zavedení smíšených tříd do hlavní třídy .
Pro použití mechanismu dědičnosti v PHP je nutné v deklaraci třídy za jménem deklarované nástupnické třídy uvést slovo extendsa název třídy předka :
class Potomek rozšiřuje předka { }Pokud se odvozená třída překrývá s metodami předka, lze k metodám předka přistupovat pomocí parent:
class A { function example () { echo "Metoda A::example() volana.<br /> \n " ; } } class B extends A { function example () { echo "Metoda B::example() volana.<br /> \n " ; rodič :: příklad (); } }Je možné zabránit odvozené třídě v přepsání metod předka; Chcete-li to provést, musíte zadat klíčové slovo final:
class A { finální příklad funkce () { echo "Volána metoda A::example().<br /> \n " ; } } class B extends A { function example () { //vyhodí chybu parent :: example (); //a nikdy se nespustí } }Aby bylo možné odkazovat na konstruktor nadřazené třídy během dědění, je nutné, aby podřízená třída byla uvedena v konstruktoru parent::__construct();[2]
Rozhraní deklaruje metody, které budou viditelné mimo třídu (veřejné).
Interní metody lze implementovat bez rozhraní. Chcete-li deklarovat další vlastnosti, použijte interface-extension v implementačním souboru.
Všechny metody v Objective-C jsou virtuální.
Příklad dědičnosti z jedné třídy a dvou rozhraní :
public class A { } public interface I1 { } public interface I2 { } public class B extends A implements I1 , I2 { }Direktiva finalv deklaraci třídy znemožňuje její dědění.
Příklad dědičnosti z jedné třídy a dvou rozhraní :
public class A { } public interface I1 { } public interface I2 { } public class B : A , I1 , I2 { }Dědění z typovaných tříd lze provést zadáním pevného typu nebo přenesením proměnné typu do zděděné třídy:
veřejná třída A < T > { } veřejná třída B : A < int > { } veřejná třída B2 < T > : A < T > { }Je také možné zdědit vnořené třídy z tříd, které je obsahují:
třída A // výchozí třída A je interní, neveřejná třída B nemůže být veřejná { třída B : A { } }Direktiva sealedv deklaraci třídy znemožňuje její dědění. [3]
Třída Parentje předkem třídy, Childjejíž metoda je přepsána public_method.
dítě = dítě . novorozenec . _ public_method #=> "Předefinovaná veřejná metoda" potomek . call_private_method #=> "Soukromá metoda předka: Soukromá metoda"Soukromé metody předka lze volat od potomků.
Třída Parentje předkem třídy, Childjejíž metoda je přepsána publicMethod.
JavaScript používá prototypovou dědičnost.
V C++ jsou konstruktory volány postupně během dědění od nejstaršího předka po nejnovějšího potomka, a naopak, destruktory jsou volány od nejnovějšího potomka po nejstaršího předka.
classFirst _ { veřejnost : First () { cout << ">>Prvni konstruktor" << endl ; } ~ První () { cout << ">>První destruktor" << endl ; } }; třída Druhá : veřejná První { veřejnost : Druhý () { cout << ">Druhý konstruktor" << endl ; } ~ Druhý () { cout << ">Druhý destruktor" << endl ; } }; třída Třetí : veřejná Druhá { veřejnost : Třetí () { cout << "Třetí konstruktor" << endl ; } ~ Třetí () { cout << "Třetí destruktor" << endl ; } }; // spuštění kódu Third * th = new Third (); odstranit th ; // výsledek výstupu /* >>První konstruktor >Druhý konstruktor Třetí konstruktor Třetí destruktor >Druhý destruktor >>První destruktor */