08. Polymorfismus a dědičnost

Download Report

Transcript 08. Polymorfismus a dědičnost

4IT101
8.přednáška
Polymorfismus
Dědičnost
Abstraktní datový typ
• tři vlastnosti ADT:
– lze definovat nové typy a ukrývat reprezentaci
těchto typů,
– lze definovat množinu operací/funkcí s těmito typy
– ADT je definován v jedné syntaktické jednotce,
vytvářet proměnné tohoto typu lze i jinde,
• výhody:
– klient není závislý na konkrétní implementaci ADT,
– klient se nemůže „vrtat“ v ADT – přistupuje pouze
prostřednictvím veřejných operací/metod; zvýšená
spolehlivost,
Objekty
• požadavky na objekty:
– abstraktní datový typ (zapouzdření, ukrývání
implementace),
• třída je typ
• instance má k sobě přiřazeny datové atributy a
metody instance,
– polymorfismus a pozdní vazba,
– dědičnost,
• překrytí metod
• jednonásobná x vícenásobná dědičnost
Pojem polymorfismus
• polymorfismus – mnohotvarost
– biologie: existence několika forem jedinců jednoho
druhu nebo populace
– programování: při stejném volání metody se provádí
různý kód. Který kód se provede závisí:
• na parametrech metod,
• na instanci (objektu), kterému je zpráva předávána,
Typy polymorfismu
přetěžování metod (overloading), též ad-hoc
polymorphism
překrývání metod (overriding), též subtype
polymorphism
parametrický polymorfismus – např. šablony v
C++,
Přetěžování metod
• více metod stejného jména, liší se
– počtem parametrů,
– typem parametrů,
– pořadím parametrů
int nextInt()
int nextInt(int n)
static String valueOf(double d)
static String valueOf(int i)
static String valueOf(boolean b)
static String valueOf(char c)
Překrývání metod
• Každá třída, která implementuje
rozhraní, překrývá všechny metody z
rozhraní.
• Překrývání metod souvisí také s
dědičností (viz dále)
Příklad: motýli a včely na
louce
• dvě třídy Motyl a Vcela
• třetí třída Louka – úkolem je napsat
metody pro přidávání motýlů a včel na
louku. Máte dva seznamy:
varianta 1 – dvě metody různých názvů:
private List<Vcela> vcely;
public
void pridejMotyla
private
List<Motyl>
motyli; (Motyl motyl) {
motyli.add(motyl);
}
public void pridejVcelu (Vcela vcela) {
vcely.add(vcela);
}
Příklad: motýli a včely na
louce
2. varianta – jedna metoda
s rozskokem dle typu parametru
public void pridej (Object o) {
if (o instanceof Vcela) {
Vcela vcela = (Vcela)o;
vcely.add(vcela);
}
else if (o instanceof Motyl) {
Motyl motyl = (Motyl)o;
motyli.add(motyl);
}
else {
throw new InvallidArgumentException(
"lze vkládat pouze motýly a včely");
}
}
Příklad: motýli a včely na
louce
3. varianta – přetížení metody
public void pridej (Vcela vcela) {
vcely.add(vcela);
}
public void pridej (Motyl motyl) {
motyli.add(motyl);
}
použití:
louka.pridej(new Vcela( ...... ));
louka.pridej(new Vcela( ...... ));
louka.pridej(new Motyl( ...... ));
louka.pridej(new Motyl( ...... ));
Příklad: motýli a včely na
louce
• dvě třídy Motyl a Vcela
• třetí třída Louka – úkolem je napsat
metody pro přidávání motýlů a včel na
louku.
varianta 4 :
•
•
ve třídě Louka pouze jeden seznam,
použití polymorfismu s využitím rozhraní (interface) – jedna
metoda pro přidávání do seznamu,
Deklarace interface
[public] interface identifikátor [extends
rozhraní1 [, rozhraní2 ...]] {
[hlavička_metody1 ;]
[hlavička_metody2 ;] …
[konstanta1;]
[konstanta2;] …
[vnořená-třída]…..
}
public interface ObyvatelLouky {
public void jednaAkce();
}
Implementace rozhraní
public class Motyl implements ObyvatelLouky {
public void jednaAkce () {
if (naKvetineSNektarem()) {
// sbirej nektar
}
else {
preletni();
<<interface>>
}
ObyvatelLouky
}
}
Motyl
Vcela
Rozhraní a subtype
polymorfismus (překrývání)
deklarace:
private List<ObyvatelLouky> obyvateleLouky;
inicializace:
obyvateleLouky = new ArrayList<ObyvatelLouky>();
metoda pro vkládání motýlů a včel:
public void pridej (ObyvatelLouky obyvatel) {
obyvateleLouky.add(obyvatel);
}
<<interface>>
Louka
ObyvatelLouky
Motyl
Vcela
Rozhraní umožňuje volbu
implementace
staticky (při překladu) i dynamicky
private List <String> slova;
v konstruktoru:
slova = new ArrayList<String>();
private List <String> slova;
v konstruktoru:
if (podmínka) {
slova = new ArrayList<String>();
}
else {
slova = new LinkedList<String>();
}
Rozhraní umožňuje předávat
metody jako parametry
• příklad rozhraní Comparator a
metoda Collections.sort()
Dědičnost mezi rozhraními
• extends v hlavičce rozhraní
<<interface>>
Iterable
<<interface>>
Collection
<<abstract>>
AbstractCollection
<<interface>>
List
<<abstract>>
AbstractList
<<interface>>
<<abstract>>
Abstract-
<<interface>>
RandomAccess
Dependency-Inversion
Principle
a) Moduly vyšší úrovně nesmí záviset na
modulech nižší úrovně. Oba typy by
měli záviset na abstrakci.
b) Abstrakce by neměla záviset na
detailech. Detaily obvykle závisí na
abstrakci.
18
Dependency-Inversion
Principle
Horní úroveň
<<interface>>
RozhraníStřed
níÚrovně
Střední úroveň
<<interface>>
RozhraníDolní
Úrovně
Dolní úroveň
19
Dědičnost v Javě
Terminologie
Rodičovská třída
Bázová třída
Nadtřída
Dceřiná třída
Odvozená třída
Podtřída
Hierarchie dědičnosti
• třídy v Javě mají stromovou strukturu,
v jejímž kořeni je třída Object
– každá třída s výjimkou třídy Object má právě
jednoho předka
– třída Object je společným (pra)rodičem všech
tříd
• Na rozdíl od některých jiných jazyků
(např. C++) jazyk Java nepodporuje
násobnou dědičnost tříd
Deklarace dědičnosti
• klíčové slovo extends
– v hlavičce třídy
public class Liska extends Zvire { …. }
public class CD extends AbstractPolozka { … }
public class Kniha extends AbstractPolozka
implements Comparable { …. }
• implementace rozhraní se uvádí až za
deklarací dědičnosti
Definice dědičnosti
• dědictví od třídy Object se uvádět nemusí,
nemá-li někdo v hlavičce uvedeného předka,
je přímým potomkem třídy Object
• třídy z nepojmenovaného (kořenového)
balíčku nemají úplný název,
a proto nemohou mít potomky v jiných
balíčcích
•
CO SE VLASTNĚ DĚDÍ?
Co se vlastně dědí?
• Co znamená výraz, že se něco dědí?
V potomkovi mohu
používat prvky předka
Když vytvořím instanci
potomka, tak mohu
používat zděděné metody
a atributy předka
Potomek obsahuje
všechny prvky (metody,
atributy) předka
Object
Matka
Dcera
Vnučka
• Aby bylo možno zabezpečit funkci všech (i
soukromých) vazeb,
obsahuje každý objekt třídy potomka jako
svoji součást podobjekt svého předka,
• Objekt potomka se nemůže začít budovat
dřív,
než bude zcela vybudován podobjekt předka
•
Postup vytváření instance
1. Načtou se soubory class do paměti
–
–
nejdříve se musí načíst soubory .class
předka
při nahrání souborů .class se inicializují
statické prvky,
2. Inicializují se datové atributy
3. Zavolá se a provede konstruktor
předka
4. Provede se tělo konstruktoru
Co z předka lze používat
(volat) v potomkovi?
•
•
•
•
•
Datové atributy
Metody
Konstruktory
Statické atributy
Statické metody
Záleží na
modifikátorech
přístupu !!!!
Co nabízí potomek ze
svého předka?
•
•
•
•
•
Datové atributy
Metody
Konstruktory
Statické atributy
Statické metody
Záleží na modifikátorech, nesmí
být překryté, pozdní vazba
Záleží na modifikátorech, včasná
vazba
Final u třídy
• V hlavičce třídy může být modifikátor
final – nemůže mít potomky
public final class String extends Object
implements Serializable,
Comparable<String>, CharSequence
PŘEKRÝVÁNÍ METOD
INSTANCÍ
Překrytá metoda
• v potomkovi je metoda se stejnou
hlavičkou (jméno a parametry), jako v
předkovi
– metoda v potomkovi překryla metodu
předka
Použití překryté verze
metody
• Překrytí není předefinování ani
přepsání překryté metody
• Při překrytí metody jinou metodou
zůstává překrytá metoda nedotčená
a můžete ji v potomkovi kdykoliv
použít
kvalifikace klíčovým slovem super
public void metoda() {
super.metoda();
….
}
Další vlastnosti překrývání
metod
• Překrývající metoda musí mít stejnou
hlavičku
(název, počet parametrů a jejich typy,
typ návratové hodnoty)
jako metoda překrývaná
• nemá-li metoda stejnou hlavičku,
nejedná se o překrytí, ale o přetížení
Příklad s účty - dědičnost
• Chceme vytvořit účet s možností
výběru do mínusu.
• bude mít stejné proměnné jako třída
Ucet a navíc proměnnou limit.
• musí se změnit metoda pro výběr z
účtu.
Příklad
účty
public
classsUcet
{ – dědičnost
private intzapouzdření
cislo;
private String vlastnik;
private double stav;
a
public void vloz (double castka) {
stav += castka;
}
public boolean vyber (double castka) {.......
}
...............................................................................
}
Příklad s účty - dědičnost
public class ZiroUcet extends Ucet {
private double limit;
public boolean vyber (double castka)
{
..........
} ..............
}
Příklad s účty - překrytí metody
boolean vyber (double castka) {
Ucet.java
if (stav < castka) {
return false;
boolean vyber (double castka) {
}
if ((stav + limit) < castka) {
else {
stav = stav – castka; return false;
}
return true;
else {
}
stav = stav – castka;
}
return true;
}
}
ZiroUcet.java
Příklad s účty - překrytí
Ucet.java
metody a zapouzdření
•
public void setStav (double novyStav){
Možnosti řešení
stav = novyStav;
} možnost změnit třídu Ucet a
– máme
doplníme do ní metody setStav()
public
booleanměnit
vyber třídu
(double
castka)
– nemáme
možnost
Ucet
a {
((getStav()
+ limit)
castka)
{
musímeifnějak
použít,
to co <je.
Použijeme
returnse
false;
metodu vloz()
záporným parametrem.
Pokud }ale v implementaci tuto možnost
else {neumožní, nejsme schopni
třída Ucet
setStav(
dědičnost
využít.getStav() – castka);
}
return true;
ZiroUcet.java
PŘETYPOVÁNÍ
REFERENČNÍCH TYPŮ,
OPERÁTOR INSTANCEOF
Přetypování
Object
• Přetypování nahoru k
předkovi
– probíhá automaticky
– instanci jakékoli třídy
lze přetypovat na typ
Object
• Přetypování dolů
– musí být v kódu
uvedeno
– jedná se o návrat k
typu, ze kterého
proběhlo přetypování
směrem nahoru
String
Přetypovávání, operátor
instanceOf
public void vypis() {
for (Ucet ucet : seznam) {
if (ucet instanceof ZiroUcet) {
ZiroUcet ziro = (ZiroUcet) ucet;
System.out.println(ziro.getVlastnik() + "\tstav: " +
ziro.getStav()+"\tlimit: "+ ziro.getLimit());
}
else {
System.out.println(ucet.getVlastnik() + "\t" +
ucet.getStav());
}
}
}
Vhodnější řešit polymorfismem !!!
DĚDIČNOST A
KONSTRUKTORY
Dědičnost a konstruktory
• Konstruktor se nedědí
• Při spuštění konstruktoru se jako
první automaticky volá konstruktor
předka, pokud neurčíme který, volá
se konstruktor bez parametru.
• Pro určení volaného konstruktoru
předka slouží klíčové slovo super
Volání konstruktoru předka,
super
public Ucet (int noveCislo, String jmeno, double castka){
cislo = noveCislo;
vlastnik = jmeno;
stav = castka;
– třída Ucet nemá konstruktor bez
}
parametru
public Ucet
(int noveCislo, String jmeno){
cislo = noveCislo;
– i =když
vlastnik
jmeno;vytváříme GiroUcet chceme určit
stav = 0;
číslo učtu, vlastníka a případně i stav
}
• Máme několik problémů
účtu, navíc určujeme limit
public ZiroUcet (int noveCislo, String jmeno, double castka,
double limit) {
super(noveCislo, jmeno,castka);
this.limit = limit;
}
public ZiroUcet (int cisloUctu, String vlastnik,
double pocatecniVklad, double limit){
super(cisloUctu, vlastnik, pocatecniVklad);
this.limit = limit;
}
public ZiroUcet (int cisloUctu, String vlastnik,
double pocatecniVklad){
this(cisloUctu, vlastnik, pocatecniVklad, 0);
}
public ZiroUcet (int cisloUctu, String vlastnik){
this(cisloUctu, vlastnik, 0, 0); //volání prvního konstruktoru
}
Na co si dát u
konstruktorů pozor
• V těle konstruktoru nesmíme volat
virtuální metody, a to ani zprostředkovaně
(tj. volat metodu, která volá virtuální
metodu)
• Pokud potomek danou metodu překryje,
může v překryvné verzi používat atributy,
které při práci rodičovského konstruktoru
ještě neexistují (přesněji nejsou ještě
inicializovány)
Na co si dát u
konstruktorů pozor
• V konstruktoru bychom proto měli
používat
pouze soukromé a konečné metody
• Potřebujeme-li použít virtuální (=
překrytelnou) metodu,
definujeme její soukromou verzi,
kterou bude volat jak konstruktor,
tak daná virtuální metoda