Scala case classes, třídy nejen pro líné programátory

V Etneteře proběhl další z workshopů pro zájemce o Scalu. Tématem byly case classes a pattern matching. V článku si shrneme poznatky o "case" třídách, šikovných třídách, které nám ušetří nejeden úhoz do klávesnice, na pattern matching se blíže podíváme v některém z budoucích příspěvků.

Case classes

Na "case" třídy, tj. docela běžné třídy definované s klíčovým slovem case, můžeme při troše zjednodušení pohlížet jako na immutable přepravky dat, jednoduché datové objekty, které out-of-box disponují všemi důležitými základními vlastnostmi, aniž bychom se v kódu museli hodně rozepisovat. Třída může být definovaná např. takto:

Vlastnosti case class

Co nám tato jednoduchá definice poskytne? Docela velké množství funkcionality na jeden napsaný řádek:

Předimplementované základní metody

Překladač automaticky vygeneruje metody hashCode, equals používající všechny atributy třídy (ve Scale se pro porovnávání objektů volá finální metoda ==, která interně volá metodu equals, kterou lze přepsat). Automaticky se generuje toString vypisující všechny atributy ve formátu: ClassName(field1,field2,...).

Parametry konstruktoru

Parametry konstruktoru jsou automaticky val, aniž bychom to v definici konstruktoru uváděli. Pro takové parametry překladač vytvoří ve třídě stejnojmenné veřejně dostupné fieldy, a můžeme tak např. volat obj.attributeName. Parametry konstruktoru nám překladač dovolí definovat i s klíčovým slovem var, potom se vygeneruje i setter, zaděláváme si tak ale na problémy - třída není immutable a přitom má vygenerované metody equals a hashCode.

Kopírování objektů

Case class má metodu copy, kterou můžeme použít pro vrácení kopie, která se  liší jen v několika málo atributech: person.copy(name="George"). Při takovém volání se využívají pojmenované a výchozí parametry metod.

Case classes a dědičnost

Case class můžeme odvodit od abstraktní nebo neabstraktní třídy, která nemá var parametry, anebo od traitu (trait nemůže mít konstruktor s parametry).

Od case class nemůžeme podědit jinou case class. Ve Scale 10.x je to nově zakázáno. Ve Scale 9.x to bylo možné, ale překladač generoval varování (není např. možné správně používat vygenerovanou metodu equals, která by nesplňovala podmínku symetričnosti - volání z předka přijímající instanci potomka a volání z potomka přijímající instanci předka se nebude chovat symetricky). Od case class můžeme ale podědit normální class, kde si všechno předefinujeme podle potřeby.

Konstrukce a dekonstrukce

Pro case class je automaticky vygenerován companion objekt, tj. objekt, který je definovaný se stejným jménem a ve stejném zdrojovém souboru jako case class (object se typicky používá např. pro definici factory metod instanciujících stejnojmennou třídu). Tento objekt má vygenerované metody apply a unapply.

Instance case class můžeme konstruovat pohodlněji bez klíčového slova new - díky existenci metody apply v companion objektu, která je překladači dobře známá. Case class můžeme automaticky používat při pattern matchingu - díky existenci metody unapply v companion objektu. Ve Scale můžeme objekty složit (zkonstruovat) pomocí metody apply a rozložit (dekomponovat na části) pomocí metody unapply.

Private atributy

Parametry konstruktoru můžeme definovat i s klíčovým slovem private val, potom ale bude ve třídě vygenerován private field, uvažovaný v implementacích metod hashCode, equals a toString. Obecně ale toto není příklad hodný následování - k jednou zadané informaci už nemáme nadále přístup. Můžeme ji však vyextrahovat pomocí metody unapply, což je jako drbat se levou rukou za pravým uchem.

Immutable?

Do case class bychom měli jako atributy předávat pouze immutable komponenty, pokud chceme, aby byla case class immutable (a to by měla, když už má vygenerovaný equals a hashCode).

Příklad mluví za vše

Vlastnosti case class si můžeme demonstrovat na jednoduchém příkladu ve Scala interpreteru (REPLu) nebo worksheetu, kde se nám hned vypíše výsledek vyhodnocení každého napsaného výrazu.

Nejdříve si nadefinujeme poněkud nestandardní case classes reprezentující osoby:

A podrobíme je několika pokusům:

Článek obsahuje 0 komentářů