Typy niejawne
Abstrakcje bez kosztu
Typy niejawne to bezpłatna lekcja Scala for Backend Engineering & Functional Programming na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Scala for Backend Engineering & Functional Programming, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Scala for Backend Engineering & Functional Programming zawiera 4 lekcji w sumie.
Czym są typy opaque?
Typy opaque to funkcja Scali 3 służąca do tworzenia abstrakcji bez kosztu. W czasie kompilacji zapewniają odrębny typ, ale kompilują się do typu bazowego bez opakowania w czasie działania.
- Bezpieczeństwo typów klasy opakowującej.
- Brak narzutu alokacji.
object Distances:
opaque type Meters = Double
object Main:
def main(args: Array[String]): Unit =
println("Opaque types compile to their underlying type")Deklarowanie typu opaque
Typ opaque należy zadeklarować wewnątrz obiektu lub klasy. Poza tym zakresem Meters i Double są traktowane jako różne typy.
object Distances:
opaque type Meters = Double
def meters(d: Double): Meters = d
def toDouble(m: Meters): Double = m
object Main:
def main(args: Array[String]): Unit =
val d = Distances.meters(5.0)
println(Distances.toDouble(d))Granica niejawności
W zakresie, w którym typ opaque jest definiowany, typ opaque i jego typ bazowy są wzajemnie zamienne. Poza nim widoczny jest tylko typ opaque, więc nie można przypadkowo przekazać zwykłej wartości Double tam, gdzie wymagany jest Meters.
object Distances:
opaque type Meters = Double
def apply(d: Double): Meters = d
def show(m: Meters): String = s"$m m"
object Main:
def main(args: Array[String]): Unit =
val m = Distances(10.0)
println(Distances.show(m))Dodawanie metod rozszerzających
Typom opaque można nadać zachowanie za pomocą metod rozszerzających zdefiniowanych w tym samym zakresie. Kod wywołujący otrzymuje wygodne operacje bez ujawniania typu bazowego.
object Distances:
opaque type Meters = Double
def apply(d: Double): Meters = d
extension (m: Meters)
def +(other: Meters): Meters = m + other
def value: Double = m
object Main:
def main(args: Array[String]): Unit =
val total = Distances(3.0) + Distances(4.0)
println(total.value)Zapobieganie pomyłkom
Najważniejsza korzyść: dwa typy opaque o tym samym typie bazowym są niezgodne. Meters nie można pomylić z Kilometers, mimo że oba są typu Double.
object Units:
opaque type Meters = Double
opaque type Kilometers = Double
def m(d: Double): Meters = d
def km(d: Double): Kilometers = d
def toMeters(k: Kilometers): Meters = k * 1000
object Main:
def main(args: Array[String]): Unit =
val far = Units.km(2.0)
println(Units.toMeters(far))Typy opaque z ograniczeniami
Typowi opaque można nadać ograniczenie górne za pomocą <:. Ujawnia to część publicznego API, zachowując ukrytą dokładną reprezentację.
object Ids:
opaque type UserId <: Int = Int
def apply(i: Int): UserId = i
object Main:
def main(args: Array[String]): Unit =
val id = Ids(99)
println(id + 1)Inteligentne tworzenie z walidacją
Połącz typy niejawne z fabryką przeprowadzającą walidację, aby mieć pewność, że wartości są zawsze poprawne. Konstruktor pozostaje prywatny, a publiczna jest tylko zwalidowana fabryka.
object Ages:
opaque type Age = Int
def of(i: Int): Option[Age] =
if i >= 0 && i < 150 then Some(i) else None
extension (a: Age) def value: Int = a
object Main:
def main(args: Array[String]): Unit =
println(Ages.of(30).map(_.value))
println(Ages.of(-1))Brak kosztu w czasie działania
W przeciwieństwie do opakowania w postaci value class lub case class typ niejawny nie tworzy w czasie działania żadnego obiektu. Lista Meters jest na JVM dosłownie listą wartości typu double, więc nie występuje boxing.
object Temps:
opaque type Celsius = Double
def c(d: Double): Celsius = d
extension (t: Celsius) def value: Double = t
object Main:
def main(args: Array[String]): Unit =
val readings = List(Temps.c(20.0), Temps.c(21.5))
println(readings.map(_.value).sum)Typ niejawny a alias typu
Zwykły alias type X = Y jest transparentny: X i Y można wszędzie stosować zamiennie. Typ niejawny ukrywa tę równość poza swoim zakresem, zapewniając rzeczywiste bezpieczeństwo typów.
object Demo:
type Name = String // transparent alias
opaque type Email = String // opaque
def email(s: String): Email = s
extension (e: Email) def raw: String = e
object Main:
def main(args: Array[String]): Unit =
val e = Demo.email("a@b.com")
println(e.raw)Typ niejawny a opakowanie w case class
Opakowanie w case class, takie jak case class Meters(value: Double), również zapewnia bezpieczeństwo typów, ale powoduje utworzenie obiektu. Typy niejawne zapewniają ten sam poziom bezpieczeństwa przy wydajności surowego typu.
object Money:
opaque type Cents = Long
def cents(n: Long): Cents = n
extension (c: Cents)
def +(o: Cents): Cents = c + o
def value: Long = c
object Main:
def main(args: Array[String]): Unit =
val total = Money.cents(150) + Money.cents(50)
println(total.value)Kiedy używać typów niejawnych
Typów niejawnych należy używać, gdy potrzebne jest silne typowanie domenowe w często wykonywanych ścieżkach kodu lub w dużych kolekcjach.
- Rozróżnianie wielkości fizycznych (Meters i Seconds).
- Wymuszanie zwalidowanych niezmienników.
- Unikanie narzutu związanego z tworzeniem opakowań.
object Geo:
opaque type Latitude = Double
def lat(d: Double): Option[Latitude] =
if d >= -90 && d <= 90 then Some(d) else None
extension (l: Latitude) def value: Double = l
object Main:
def main(args: Array[String]): Unit =
println(Geo.lat(41.0).map(_.value))Szybkie sprawdzenie
Sprawdź swoją wiedzę na temat typów niejawnych.
Podsumowanie
Poznał(a) Pan/Pani typy niejawne w Scala 3.
opaque type X = Yukrywa równość poza swoim zakresem.- Udostępniaj fabryki i metody
extensionjako API. - Różne typy niejawne oparte na tym samym typie bazowym są niekompatybilne.
- Opcjonalne ograniczenia górne z użyciem
<:udostępniają część API. - Brak kosztu w czasie działania, w przeciwieństwie do opakowań w case class.
object Ids:
opaque type OrderId = String
def apply(s: String): OrderId = s
extension (o: OrderId) def raw: String = o
object Main:
def main(args: Array[String]): Unit =
val id = Ids("ORD-1")
println(id.raw)Często zadawane pytania
Czy lekcja „Typy niejawne” jest bezpłatna?
Tak — pełny tekst „Typy niejawne” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Scala for Backend Engineering & Functional Programming, przejdź na CoddyKit PRO. Kurs Scala for Backend Engineering & Functional Programming zawiera 4 lekcji w sumie.
Co nauczysz się w „Typy niejawne”?
Abstrakcje bez kosztu Ćwiczysz Scala for Backend Engineering & Functional Programming z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Scala for Backend Engineering & Functional Programming?
Nie wymagamy żadnego doświadczenia. Scala for Backend Engineering & Functional Programming w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.
Ile czasu zajmuje lekcja „Typy niejawne”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Scala for Backend Engineering & Functional Programming?
Tak. Każda lekcja Scala for Backend Engineering & Functional Programming zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.