0Pricing
Scala for Backend Engineering & Functional Programming · Lekcja

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 = Y ukrywa równość poza swoim zakresem.
  • Udostępniaj fabryki i metody extension jako 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.

Wszystkie lekcje w tym kursie

  1. Znaczące wcięcia
  2. Enumy w Scali 3
  3. Typy niejawne
  4. Typy sumy i przecięcia
← Powrót do Scala for Backend Engineering & Functional Programming