Seminář 4
Aktualizace
- Přidány kódy ze semináře plus něco navíc, ať si to můžete projít v klidu i sami.
- Rozsáhlejší příklad
- Záznamy
- V obou kódech je mnoho komentářů k osvětlení toho, co se tam vlastně děje.
Úkoly
- Je zvykem do git necommitovat pro sestavení zbytečné soubory (IDE generované, vlastní poznámky, …).
V Github desktop lze zaškrtnout, co chcete do commitu přidat a co ne. Případněgit add
agit rm
v CLI. - Kdy používat klíčové slovo
this
? - Problémy? Otázky?
OOP podruhé
Dnes nás čeká:
- Principy:
- polymorfismus,
- zapouzdření,
- dědičnost.
- Prvky jazyka:
- modifikátory přístupu
private
,protected
apublic
, - rozhraní (interface),
- abstraktní třídy a metody. Klíčové slovo
abstract
, - klíčová slova
extends
,implements
,instanceof
asuper
, - anotace
@Override
, - Záznamy (record).
Narazíme i na:
- Klíčové slovo
final
. Konstanty, omezení dědičnosti. - Klíčové slovo
static
. Entity příslušné celé třídě (nikolv konkrétní instanci). - Klíčové slovo
default
. Implementace funkcionality v rozhraní.
Polymorfismus
- Na objekt lze nahlížet z více úhlů pohledu - může být instancí více různých tříd a rozhraní (s omezeními, vizte dále). Např. jeden člověk může být úředník, manžel i skaut - a dle aktuální situace tedy mít různé vlastnosti a možnosti.
- Více tříd může mít stejné metody. Např. metodu
toString()
mají všechny objekty (všechny instance všech tříd). - Metoda jednoho názvu může mít více verzí s různými seznamy argumentů (už jste viděli).
Zapouzdření
- Vnitřní a vnější rozhraní objektu
- Viditelnost dané entity ovlivněna modifikátory přístupu:
private
- k této entitě má přístup jen objekt samotnýprotected
- k této entitě má přístup cokoliv v rámci stejného balíčku a případní potomci mimo balíčekpublic
- k této entitě má přístup kdokoliv (veřejná API balíčku)- default - k této entitě má přístup cokoliv v rámci stejného balíčku. Výchozí modifikátor, nepíše se.
- Ještě se k tomu jednou vrátíme u modulů.
- Pozor je to jinak než v C#.
- Speciální metody pro kontrolovaný přístup k atributům:
- čtení - getter
- zápis - setter
- může jich být více (polymorfismus) anebo taky nemusí být žádný
- Ukázka: Máme třídu
Car
odkaz, podobnou té z minula. Upravíme ji tak, aby:- Rychlost šla nastavoavat jen v rozsahu min-max rychlosti auta.
- Nabírání a vysazování posádky bylo možné, jen když auto stojí.
- SPZ nešla po vytvoření změnit vůbec.
- Všimněte si, že v kódu třídy
Car
můžu s danou instancí stále dělat, co chci - je má zodpovědnost, aby to dávalo smysl. Ale uživatel této třídy už může používat jen mnou nadefinované veřejné rozhraní, a tedy nemůže například vysadit posádku ve 100 Km/h.
Dědičnost
Pravidlo … is a … → dědičnost
Pravidlo … has a … → kompozice
Příklad: Co kdybychom do našeho dopravního systému chtěli přidat i motorky, kola, vlaky, …?
- Třídy
Motorbike
,Bicycle
,Train
,Bus
,Tram
, … - Mají toho mnoho společného, abstrahujme tedy společné do třídy
Vehicle
- dopravní prostředek. Všechny třídy výše pak budou dědit zVehicle
a jen doplní svá specifika.
- Třídy
V kódu potomka se dá přistupovat k metodám a (přístupným) atributům předka přes klíčové slovo
super
.Třída může překrýt implementaci metody předka. Pokud jde o instančí metodu, pak je přepsána a k metodě předka už se přes daný objekt (mimo kód potomka a
super
) nedostaneme. Přepis (čehokoliv z předka) se označuje anotací@Override
.Dědičnost se dá omezit
- Označíme-li třídu klíčovým slovem
final
, tak z ní nepůjde dále dědit. - Označíme-li metodu klíčovým slovem
final
, tak nepůjde překrýt. - Od Java 17 máme sealed třídy. Sofistikovanější mechanizmus pro omezení dědičnosti (lze omezit, kdo může dědit z naší třídy).
- Označíme-li třídu klíčovým slovem
Každá třída může dědit z nejvýše jedné třídy. Klíčové slovo
extends
.Rozhraní (interface)
- Konstrukt jazyka. Pozor, souběh pojmů - něco jiného než vnitřní a vnější rozhraní objektu.
- Umožňuje vynutit, aby nějaké třídy implementovaly nějaké metody (kontrakt). Typicky nějaká abstraktní vlasnost, kterou mohou mít různé spolu vůbec nesouvisející třídy.
- Příklad rozhraní Comparable zajišťuje, že třída implementuje vše potřebné k porovnávání jejich instancí.
- Typicky nejsme schopni říct, jak to jendotlivé konkrétní třídy budou dělat. Tedy konkrétní implementace těchto metod může být úplně odlišná pro různé třídy.
- Příklad:
- Máme sposutu různých dopravních prostředků, ale jen některé si člověk může nachystat k jízdě sám.
- Rozhraní
SelfMaintanable
s metodouprepareToUse()
aisReady()
. Je něco úplně jiného nachystat kolo, motorku nebo auto a rozhodně si člověk sam nenachystá autobus nebo vlak. TedyMotorbike
,Car
,Bicycle
budou implementovat rozhraníSelfMaintanable
, ale každý úplně po svém.Train
,Bus
aTram
rozhraníSelfMaintanable
implementovat nebudou.
Interface v Javě nemůže obsahovat žádné atributy kromě konstant. Veřejné třídní (static) metody mohou mít implementaci, veřejné instančí metody také, ale musí být označeny klíčovým slovem
default
. Soukromé metody mohou mít implementaci.Interface může dědit z jiný interface (i více) pomocí
extends
.Každá třída může implementovat více rozhraní. Klíčové slovo
implements
.Rozhraní nemůže být
final
, ale podobně jako sealed třídy existují od Java 17 i sealed rozhraní.Instance rozhraní je každá instance každé třídy, která toto rozhraní implementuje.
Abstraktní třídy
Jsou i situace, kdy spostu funkcionality je společné a chceme ji tedy definovat na jednom míste v nějaké nadřazené třídě, ale přesto nechceme/nemůžeme dovolit vytváření instancí přímo této nadřazeneé třídy. Třeba protože nějakou funkcinalitu vyžadujeme, ale její konkrétní implememtaci můžeme dodat, až po vybrání konkrétního typu odvozeného od této nadřazené třídy.
Podobně se může stát, že část funcionality jsme schopni udělat už rovnou obecně, ale část funkcinality, kterou vyžadujeme, musí dodat až potomci. Tedy chceme zajistit, aby každý potomek měl nějakou metodu, ale nevíme, jakým způsobem to jendotlivý potomci udělají. Jinými slovy známe hlavičku této metody, ale neznme jeí tělo. Metodu tedy přidáme jako abstraktní - přidáme klíčové slovo
abstract
a nevytvoříme ji žádné tělo (místo těla napíšeme jen středník). Pokud je ve třídě abstraktní metoda, tak i třída musí být abstraktní.Příklad: Naše základní třída
Vehicle
. Nechceme, aby se nám v systému vyskytovaly dopravní prostředky neznámého typu, ale zároveň spoustu společné funkcionality můžeme implementovat už zde. TříduVehicle
tedy označíme za abstraktní klíčovým slovemabstract
. Tím zamezíme vytváření přímo jejich instancí - všechny instance budou instance nějakého jejího konkrétního potomka.A proč ne
SelfMaintanable
jako abstraktní třídu dědící zVehicle
a ze které bychom pak podědili doMotorbike
,Car
aBicycle
?
Pár argumentů:- Je to hodně obecná vlastnost, kterou zdaleka nemusí mít pouze dopravní prostředky (žehlička, sáňky, příbor, stream, instalátor, …).
- Reálně by v té třídě byly navíc pouze hlavičky metod
prepareToUse()
aisReady()
, tedy bychom pouze zaručovali existenci daného rozhraní objektu, ale nic z něj neimplementovali. - Je možnost samostané udržby definující vlasntost nějakého typu vozidel?
Reálně se ale programátor vždy rozhoduje sám, jestli je podle něj lepší použít abstraktní třídu nebo interface.
Abstraktní třídy nemohou být
final
.
instanceof
- Jak zjistit, jestli daný objekt je instancí dané třídy (zahrnuje i, že je instancí potomka) nebo jestli implementuje dané rozhraní? Klíčové slovo
instanceof
.
- Jak zjistit, jestli daný objekt je instancí dané třídy (zahrnuje i, že je instancí potomka) nebo jestli implementuje dané rozhraní? Klíčové slovo
Záznamy (Record)
- Popis v release notes.
- Slouží k jednoduché agregaci souvisejících dat.
- Je to vlastně omezená třída s jednodušším zápisem a konstantním obsahem.
- Dodáme název a seznam atributů, zbytek (gettery, konstruktor, toString(), equals(…), hasCode(…)) doděla překladač.
- Vše dodělané překladačem můžeme přepsat (konstruktor i ve zkrácené formě), ale pak za to ručíme sami.
- Můžeme přidat další libovolný
static
obsah a instanční metody (avšak ne instanční atributy). - Všechny třídní atributy budou vždy
final
, tedy nemá smysl uvažovat settery. - Každý record je implicitně
final class
, tedy z nich nelze dále dědit. - Lze s nimi pracovat lokálně.
- Další vlastnosti zatím mimo naše možnosti - pro zájemce odkaz výše.
(ukázka)
Úkol
- Zadání je v readme v repozitáři.
- Termín za týden. Plus případný týden na opravy.
- Odkaz.