Kurzusok
/
Crystal
Crystal
/
Tanterv
/
Uniótípus
Un

Uniótípus ebben a kurzusban: Crystal

1 feladat

A(z) Uniótípus fogalomról

A Crystal megengedi, hogy egy változó több típusból álljon. Ezt nevezik unió típusnak. A Crystalben gyakori, hogy az unió típust a fordító következteti ki.

Note

Az unió típus futásidőben továbbra is egyetlen típus, még akkor is, ha több típusból áll. Ez azt jelenti, hogy ha egy unió típus String-ből és Int32-ből áll, akkor nem lesz egyszerre mindkettő. Ehelyett vagy String, vagy Int32 lesz.

Az unió típust úgy deklaráljuk, hogy a típusokat egy függőleges vonallal (|) választjuk el. Gyakran zárójelbe teszik őket, de erre nincs feltétlenül szükség. A leggyakoribb unió típus a (T | Nil), ahol T egy típus; ez olyan metódusokban fordulhat elő, amelyek Nil-t adhatnak vissza. Ezt úgy is szokás nevezni, hogy a változó nilable.

foo : String = "Hello"
foo = nil # Error: type must be String, not (String | Nil)

foo : (String | Nil) = "Hello"
foo = nil

Nem csak két típusra korlátozódik, hanem annyiból állhat, amennyiből csak szeretnéd.

foo : (String | Int32 | Nil | Float64) = "Hello"
foo = 1
foo = nil
foo = 1.0

typeof vs Object#class

Kétféleképpen tudhatod meg egy változó típusát. Vagy a typeof, vagy az Object#class használatával. A különbség, hogy a typeof a fordítás idején adja vissza a változó típusát, míg az Object#class futásidőben. Ez azt jelenti, hogy ha például azt szeretnéd megnézni, hogy egy változó unió típusú-e, azt az Object#class nem fogja megmondani, mert csak a futásidejű típust adja vissza, ami egyetlen típus.

foo = 0 == 0 ? "a" : 1
typeof(foo) # => (String | Int32)
foo.class # => String

Műveletek unió típusokkal

Mivel az unió típus futásidőben egyetlen típus, minden szokásos művelet működik rajta. A kód fordítása során azonban a fordítónak tudnia kell, melyik típusról van szó. Ezért a kódot úgy kell megírni, hogy csak az egyik típus lehessen, amikor típusspecifikus műveleteket szeretnél használni.

foo : (String | Int32) = "Hello"
foo.downcase # Error: undefined method 'downcase' for (String | Int32)

A Crystalnek van egy különleges metódusa az unió típusokhoz: az is_a? metódus, amely egy típust vár argumentumként, és egy logikai értéket ad vissza. A nil? metódus az is_a?(Nil) rövidítése. Ha az is_a? metódust egy vezérlő kifejezésbe teszed, az megmondja a fordítónak, melyik típusról van szó, és ezáltal garantálja, hogy az a típus. Az else ágban pedig garantált lesz, hogy nem az a típus.

foo : (String | Int32) = "Hello"
if foo.is_a?(String)
  typeof(foo)  # => String
  foo.downcase # => "hello"
end

Ez az is_a? nincs arra korlátozva, hogy egyetlen típust kapjon argumentumként, hanem unió típust is kaphat. Több típusnál a && operátorral is kombinálható.

Note

Az is_a? metódus vezérlő kifejezéssel együtt használva nem lehet példányváltozó vagy osztályváltozó. Ehelyett ezeket előbb egy lokális változóhoz kell rendelni.

as

Az egyik módja annak, hogy egy unió típust egyetlen típussá alakítsunk, hogy elérjük: egy ág csak akkor legyen végrehajtható, ha a típus egy adott típus. Egy másik megközelítés az as metódus használata. Ez egy futásidejű ellenőrzéssel alakítja az unió típust egyetlen típussá. Ha a típus nem a várt típus, kivétel keletkezik.

foo : String | Int32 = "Hello"
foo.as(String).downcase # => "hello"

foo.as(Int32) # Error: can't cast String to Int32
Caution

Ez a megközelítés csak akkor használható, ha biztos vagy benne, hogy a típus a várt típus, vagy ha kivételt szeretnél kapni, amikor nem az.

Ha ezt a megközelítést nem megfelelő beállítások mellett használod, váratlan viselkedéshez vezethet.

as?

Az as? nagyon hasonlóan működik, mint az as, de ha a típus nem a várt típus, kivétel dobása helyett nil-t ad vissza. Ez azt jelenti, hogy a várt típus és a Nil unió típusát adja vissza.

foo : (String | Int32) = "Hello"
foo.as?(String).downcase # => "hello"

foo.as?(Int32) # => nil

A nilable rövidítés

A nilable azt jelenti, hogy egy változó vagy egy adott típus, vagy Nil lehet. Ezt (T | Nil) alakban írhatjuk. De mivel a nilable típusok viszonylag gyakoriak, van rájuk egy rövidítés: T?.

# This:
foo : (String | Nil) = "Hello"
foo = nil

# Is the same as:
foo : String? = "Hello"
foo = nil

Unió típusok kikövetkeztetése

A fordító kikövetkeztet egy unió típust, ha nem biztos benne, melyik típusról van szó. Például a következő kódban a fordító nem fogja tudni, hogy a foo melyik típusú, mivel az vagy String, vagy Int32 lehet.

if true
  foo = 1
else
  foo = "Hello"
end

typeof(foo) # => (Int32 | String)

Ez a következtetés automatikusan történik, és vannak más esetek is, amikor például a fordító azt következteti ki, hogy egy metódus unió típust ad vissza.

character = "Hello world"[10]?
typeof(character) # => (Char | Nil)
Szerkesztés GitHubon A hivatkozás új ablakban vagy lapon nyílik meg

Tanuld meg a(z) Uniótípus fogalmat

A gyakorlás zárolva

Oldj fel még 1 feladatot, hogy gyakorolhasd a(z) Uniótípus fogalmat