3. díl - Swift - Třídy, Enumerátory, Struktury

Ostatní jazyky Swift Swift - Třídy, Enumerátory, Struktury

Nil a volitelné typy

Někdy se nám stane, že u funkce třeba nechceme (ale můžeme) vrátit hodnotu. Od toho existují tzv. volitelné typy.

Zapisují se takto: DatovýTyp? anebo takto DatovýTyp!

Rozdíl vám hned vysvětlím:

Otazník se píše tam, kde může hodnota proměnné být nil (nil = null - nic) kdykoli.

Vykřičník se píše tam, kde je proměnná nil pouze jednou a později by už této hodnoty nabýt neměla.

Takže příklad volitelné proměnné:

var pozdějiToPoužiju: String! = nil // Tato proměnná pravděpodobně už nikdy nebude nil.
var totoBudeRůznorodé: AnyObject? = nil // Tato proměnná se pravděpodobně nilem ještě mnohokrát stane.

Na začátku jsem psal o volitelném Returnu - zde je příklad:

func funkce(parametr: Int) -> String? {
    if parametr > 10 {
        return "Větší než deset."
    }

    return nil // Return ani nil nesmíme vynechat, protože jsme jasně řekli, že funkce něco vrací
}

Stejně tak můžou být volitelné parametry:

func funkce(parametr: Int?) -> String {
        if let param = parametr { // If-Let vysvětlím za chvíli.
                return "Existuje, \(param)." // Formátování textu.
        }

        return "Neexistuje, nil."
}

funkce(13) // Existuje, 13.
funkce(nil) // Neexistuje, nil. --- Volitelné parametry nesmíme vynechat, nejsou "volitelné" v tom pravém slova smyslu.

Poznámka: Do textu můžeme vložit hodnotu proměnné, funkce, atd. pomocí \(...)

A také téměř ekvivalentní možnost k předchozímu příkladu:

func funkce(parametr: Int?) -> String {
    if parametr != nil {
        return "Existuje, \(parametr)."
    }

    return "Neexistuje, nil."
}

funkce(13) // Existuje, Optional(13).
funkce(nil) // Neexistuje, nil.

Druhý zápis nám vyhodil Optional, protože jsme četli přímo z volitelné konstanty. Proto je první zápis lepší.

To by bylo k deklaracím proměnných.. U získávání hodnot z volitelných proměnných nebo získávání jejich atributů je to ovšem větší "magie".

Mějmě proměnnou:

var prom: Třída? = Třída()

Tato proměnná není (jak by se zdálo) typu Třída, ale Optional(Třída). Obrazně: Hodnota volitelné proměnné je "zabalena" v obalu, ve kterém může (a nemusí) něco být. Technicky: Volitelný typ je vlastně enum, který nabývá buď hodnoty (Třída(...)) nebo "ničeho" (nil).

Kvůli tomuto je potřeba při získávání atributů nejdříve proměnnou "rozbalit", protože typ enum nemá náš atribut.

Existují dva způsoby:

  1. Vynucené rozbalení - pokud je proměnná nil, vyhodí chybu, jinak vrátí hodnotu:
pole!.atribut
  1. Nenucené rozbalení - pokud je proměnná nil, vrátí nil (a ignoruje zbytek), jinak vrátí hodnotu:
pole?.atribut

If-Let

Pokud potřebujeme zjistit, zda proměnná není nil a pokud ne, tak jí použít, použijeme If-Let. Tato konstrukce vytvoří konstantu použitelnou pouze uvnitř těla If. Je to také jediná podmínka, která obsahuje přiřazení.

Ku příkladu:

func funkce(parametr: AnyObject?) -> AnyObject {
    if let param = parametr {
        return param
    }

    return "Neexistuje."
}

Jinak můžeme také použít If-Var a bude to také fungovat, pouze se vytvořená proměnná může měnit.

Struktury

V programovacích jazycích je standard umožnit programátorovi vytvářet jeho vlastní datové typy. Ostatně, v podstatě všechny typy ze standardní knihovny Swiftu (String, Int, Bool, ...) jsou vytvořeny podobným způsobem.

Ty jednoduché datové typy se vytvářejí pomocí struktur. Struktura se zapisuje takto:

struct Jméno {
   // Atributy a metody
}

Atributy jsou proměnné, u kterých se většinou neuvádí hodnota, ale pouze datový typ. Metody jsou funkce, které můžeme volat pomocí tečky:

var potomek: Struktura = Struktura() // Všimněte si zápisu konstruktoru.

potomek.funkce()

Zkusme si vytvořit jednoduchou strukturu:

struct Člověk {
    var jméno: String
    var věk: Int
    var muž: Bool

    func řekniInfo() {
        print("Jsem \(self.jméno) a mám \(self.věk) let.") // Funkce 'print' vypisuje na standardní výstup. `self` je zástupná proměnná zastupující danou instanci.
        // Z minula známe zápis '\(...)', kterým se vkládají do textu (nejen) proměnné.
    }
}

let karel: Člověk = Člověk(jméno: "Karel Novák", věk: 45, muž: true)
karel.řekniInfo() // Vypíše: 'Jsem Karel Novák a mám 45 let.'

Všimněte si, že Swift nám vytvořil inicializátor sám. Pokud by se nám však nelíbilo, že musíme uvést všechny atributy, můžeme si vytvořit inicializátor vlastní.

A to takto:

init(atribut: AnyObject) {
    self.nějakýAtribut = atribut
}

Init funguje stejně jako funkce, jen při volání nebudeme psát Struktura.init(...) ale klasický konstruktor.

Třídy

Třídy fungují stejně, až na to, že při instanciaci (vytvoření proměnné) se struktura kopíruje (pro Javisty, je to hodnotový typ), zatímco třída předává odkaz - referenci (referenční typ).

class Člověk {
    var jméno: String, věk: Int, muž: Bool // Pomocí jediného řádku můžeme deklarovat více proměnných/konstant.
}

Enumerátory

Pokud nechceme vytvářet plnohodnotný typ, který má své atributy, ale jen jednoduchý výčet (např. BarvaKarty nabývala právě čtyř hodnot - Piky, Kříže, Srdce a Káry), použijeme Enumerátor.

Enumerátor - výčtový typ - slouží jako seznam hodnot, z něhož nabývá proměnná právě jedné hodnoty.

Ten se zapisuje takto:

enum Karta {
    case Srdce
    case Káry
    case Piky
    case Kříže
}

Přičemž jednotlivé možnosti (case) je možné spojit takto:

enum Karta {
    case Srdce, Káry, Piky, Kříže
}

Enumerátory mohou obsahovat i funkce:

enum Karta {
    case Srdce, Káry, Piky, Kříže

    func jakáKarta() {
        print("Mám barvu \(self.hashValue)") // HashValue vrací Int, pořadí hodnoty výčtu.
    }
}

Hodnoty enumerátoru také můžou mít hodnoty různých typů:

enum Karta: String { // Stejným způsobem dědí třídy
    case Srdce = "srdcová", Káry = "kárová", Piky = "piková", Kříže = "křížová"

    func jakáKarta() {
        print("Jsem \(self.rawValue) karta.") // RawValue je primitivní hodnota svázaná k hodnotě výčtu.
    }
}

Bylo by velice zdlouhavé psát u změny hodnot jméno výčtů stále dokola:

var výčet = VeliceŠpatněZvolenéJménoVýčtu.Výčet1

výčet = VeliceŠpatněZvolenéJménoVýčtu.Výčet2
výčet = VeliceŠpatněZvolenéJménoVýčtu.Výčet3

výčet = VeliceŠpatněZvolenéJménoVýčtu.Výčet1
// ...
výčet = VeliceŠpatněZvolenéJménoVýčtu.Výčet15000

A proto, pokud máme určený typ, můžeme zapsat zkrácenou verzi:

var výčet: VeliceŠpatněZvolenéJménoVýčtu = .Výčet1

výčet = .Výčet2
výčet = .Výčet3

výčet = .Výčet1

Switch

Tato konstrukce je užitečná zejména u enumerátorů. Můžeme s ní nahradit tuto konstrukci if-else:

if výčet == .Výčet1 {
    // ...
} else if výčet ==.Výčet2 {
    // ...
} else if výčet ==.Výčet3 {
    // ...
} else if ...

Zapisuje se následovně:

switch proměnná {
    case hodnota:
        // ...

    case jináHodnota:
        // ...

    case hodnota1, hodnota2:
        // ... (proměnná má hodnotu 1 nebo 2, neomezeno počtem hodnot)

    default:
        // Sem program skočí, pokud žádná hodnota v 'case' nebyla nabyta. Default je (u většiny typů) povinné
}

U enumerátorů se může napsat již zmíněný zkrácený zápis:

switch výčet {
    case .Výčet1:
        // ...

    case .Výčet2:
        // ...
}

A u Intů můžeme využívat intervaly:

let číslo: Int = 3

switch číslo {
    case 1...5:
        print("Od jedné do pěti!")
}

Použití break na konci case není povinné (používá se občas pro zpřehlednění).

Subskripty

Subskripty jsou "zkráceniny" pro přistupování k objektům nebo částem (elementům) objektů. Mohou být definovány v třídách, strukturách a enumeracích. Pro pokročilé: Při volání subskriptu používáme syntaxi indexů.

Pro méně pokročilé, subskripty voláme takto:

objekt[argument]

Mají tedy stejnou syntaxi jako přistupování k položkám v poli. Pole a jiné kolekce to dělají také tak.

Nadefinujme si jednoduchou třídu:

class Seznam {
    var sez: [AnyObject]

    init(sez: AnyObject...) {
        self.sez = sez
    }
}

A nyní do něj přidáme subskript:

class Seznam {
    var sez: [AnyObject]

    init(sez: AnyObject...) {
        self.sez = sez
    }

    subscript(index: Int) -> AnyObject {
        if index >= self.sez.length {
            print("Index přetekl přes délku seznamu!")
        } else if index < 0 {
            print("Index musí být větší než -1!")
        } else {
            return self.sez[index]
        }
    }
}

Subskript nyní použijeme:

let seznam = Seznam("Ahoj světe", true, Seznam("Ahoj!"))
print(seznam[0])

V příštím díle si povíme něco o typovém systému.


 

  Aktivity (3)

Článek pro vás napsal jan.ruzicka01
Avatar
Autor se věnuje programovaní v Pythonu, Ruby, Javě a trochu C#. Dříve tvořil aplikace v C/C++. Nyní se zajímá o webové technologie a tvorbu virtuálních strojů (/programovacího jazyka).

Jak se ti líbí článek?
Ještě nikdo nehodnotil, buď první!


 


Miniatura
Předchozí článek
Swift - Pole, funkce, operátory
Miniatura
Všechny články v sekci
Swift
Miniatura
Následující článek
Swift - Typový systém a chyby

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!