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.
- 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, která pomocí zapouzdření zajistí aby:- Rychlost šla nastavovat 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.
Statické metody a atributy
Dosud jsme všechny metody a atributy definovali jako instační. Atributy a metody
byli asociované s instancí dané třídy. Každá instance měla jiné hodnoty atributů se kterými
pak prováděli svůj kód metody. V Javě ale můžeme definovat i atributy a metody sdílené všemi
instancemi. Atrobut tedy náleží třídě místo instance. Aby metoda (atribut) byla sdílená všemi instacemi,
tak k tomu použijeme klíčové slovo static
. Na tyto metody (atributy) se odkazujeme
pomocí jména třídy.
Příklad
public static class Person {
// Vnorena trida
// Ukazeme si hezci zpusob.
public static class Status {
public static final String SENIOR = "Senior";
public static final String JUNIOR = "Junior";
public static final String ADULT = "Adult";
}
private static final int JUNIOR_AGE_THRESHOLD = 18;
private static final int SENIOR_AGE_THRESHOLD = 65;
private int age;
public Person(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public boolean setAge(int age) {
if (age < 0) { return false; }
this.age = age;
return true;
}
public String getStatus() {
if (this.age <= JUNIOR_AGE_THRESHOLD) {
return Status.JUNIOR;
} else if (this.age <= SENIOR_AGE_THRESHOLD) {
return Status.SENIOR;
}
return Status.ADULT;
}
public static int[] getAgeThresholds() {
return new int[] {JUNIOR_AGE_THRESHOLD, SENIOR_AGE_THRESHOLD};
}
}
public static void main(String[] args) {
// Prace s vnorenou tridou
Person person = new Person(33);
if (person.getStatus().equals(Person.Status.SENIOR)) {
System.out.println("Osoba je senior");
} else if (person.getStatus().equals(Person.Status.JUNIOR)) {
System.out.println("Osoba je junior");
} else {
System.out.println("Osoba je dospely");
}
// Prace se statickou metodou
int[] ageThresholds = Person.getAgeThresholds();
}
Poznamka k porovnavani retezcu
Pokud chceme porovnávat adresy (mista kde jsou objekty uloženy), tak použijeme operátor
==
. V případě, že potřebujeme porovnávat obsah objektu, tak musíme použít metodu
equals
. Řetězce v příkladu níže jsou uloženy následovně:
- Řetězce
s1
as2
jsou uloženy v tzv Java String Pool a obě proměnné odkazují na stejnou hodnotu v paměti prot je výsledek operace==
vždytrue
. - Hodnota
s3
je ale alokována na haldě a jedná se tedy o jinou instanci.
String s1 = "HELLO";
String s2 = "HELLO";
String s3 = new String("HELLO");
System.out.println(s1 == s2); // true
System.out.println(s1 == s3); // false
System.out.println(s1.equals(s2)); // true
System.out.println(s1.equals(s3)); // true
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
.
Abstraktní třídy
- Jsou i situace, kdy spostu funkcionality je společné a chceme ji tedy definovat na jednom místě 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 funkcionalitu 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. - 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
.
- 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
.
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. - Další vlastnosti zatím mimo naše možnosti - pro zájemce odkaz výše.
Kompletni příklad na dědičnost a rozhraní si můžete stáhnout zde.
Úkoly
Implementujte třídu Point
představující bod v rovině určený dvěma souřadnicemi.
V tříde Point
implementujte metodu double distance(Point p)
vracející vzdálenost od daného bodu.
Implentujte třídu Line
představující úsečku. Implementujte metody: double getLength()
a double distance(Point p)
. Vzdálenost bodu od úsečky berte v tomto případě jako nejmenší vzdálenost mezi bodem a všemi body ležícími na úsečce.
Implementujte třídu Rectangle
představující obdelník, který má strany vodorovné s osami X a Y. Třída by měla mít dva konstruktory, jeden vytvářející obdelník pomocí dvou bodů, daląí vytvářející obdelník na základě bodu, výąky a ąířky.
Implementujte metodu double getArea()
vracející obsah daného obrazce.
Implementujte metodu double distance(Point p)
vracející vzdálenost od daného bodu. Vzdálenost bodu od obdelníku berte v tomto případě jako nejmenší vzdálenost mezi bodem a všemi body ležícími na stranách obdelníku.
Implementujte třídu Square
představující čtverec. Třída by měla mít konstruktor vytvářející objekt na zákládě souřadnice a délky strany.
Implementujte metodu double getArea()
.
Implementujte metodu double distance(Point p)
vracející vzdálenost od daného bodu.
Implementujte třídu Circle
představující kruľnici. Třída by měla mít konstruktor vytvářející objekt na zákládě souřadnice a poloměru.
Implementujte metodu double getArea()
.
Implementujte metodu double distance(Point p)
vracející vzdálenost od daného bodu. Vzdálenost bodu od kružnice berte v tomto případě jako nejmenší vzdálenost mezi bodem a všemi body ležícími na kružnici. Vzdálenost nemůže mít zápornou hodnotu!
Třídy z předchozích příkladů upravte tak, aby se díky kompozici a dědičnosti omezila redundance kódu. Pokud to má opodstatnění, použijte rozhraní.