Newtypes
Typsäkra omslag
Newtypes är en gratis lektion i Scala för backendutveckling och funktionell programmering på CoddyKit. Detta är lektion 3 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för Scala för backendutveckling och funktionell programmering, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Scala för backendutveckling och funktionell programmering innehåller totalt 4 lektioner.
Typsäkra wrappers
En newtype är en separat typ som kapslar in ett enda underliggande värde. Den förhindrar att värden med samma primitiva representation blandas ihop, till exempel UserId och ProductId som båda är Int.
case class UserId(value: Int)
case class ProductId(value: Int)
object Main:
def main(args: Array[String]): Unit =
val u = UserId(1)
val p = ProductId(1)
println(u.value == p.value) // values equal, types differProblemet med primitive obsession
Primitive obsession innebär att String och Int överanvänds för domänbegrepp. Det tillåter orimligheter som att skicka en e-postadress där ett namn förväntas. Newtypes löser detta genom att ge varje begrepp en egen typ.
case class Email(value: String)
case class City(value: String)
object Main:
def greet(c: City): String = s"Welcome to ${c.value}"
def main(args: Array[String]): Unit =
println(greet(City("Berlin")))Newtypes med case class
Den enklaste newtype-varianten är en case class med ett enda fält. Den ger likhetsjämförelse, en lättläst toString och mönstermatchning direkt. Nackdelen är en objektallokering per värde.
case class OrderId(value: String)
object Main:
def main(args: Array[String]): Unit =
val id = OrderId("ORD-42")
println(id)
println(id.value)Newtypes med opak typ
För noll allokering kan Ni implementera en newtype med en opak typ. Den fungerar som den underliggande typen vid körning men är separat vid kompilering.
object Ids:
opaque type UserId = Int
def apply(i: Int): UserId = i
extension (u: UserId) def value: Int = u
object Main:
def main(args: Array[String]): Unit =
val u = Ids(7)
println(u.value)Lägg till beteende med extension-metoder
Ge en newtype operationer genom extension-metoder och exponera endast det som är meningsfullt för domänbegreppet.
object Money:
opaque type Cents = Long
def apply(n: Long): Cents = n
extension (c: Cents)
def +(o: Cents): Cents = c + o
def toDollars: Double = c / 100.0
object Main:
def main(args: Array[String]): Unit =
val total = Money(250) + Money(750)
println(total.toDollars)Newtypes i funktionssignaturer
Newtypes gör signaturer självförklarande och upptäcker fel i argumentordningen vid kompilering. Ni kan inte råka byta plats på två parametrar med olika newtypes.
case class Width(value: Int)
case class Height(value: Int)
object Main:
def area(w: Width, h: Height): Int = w.value * h.value
def main(args: Array[String]): Unit =
println(area(Width(4), Height(5)))Newtypes och samlingar
En Map med en newtype som nyckel är tydligare och säkrare än en Map med råa Int som nycklar. Typen förhindrar att fel sorts nyckel används.
case class UserId(value: Int)
object Main:
def main(args: Array[String]): Unit =
val names = Map(UserId(1) -> "Ada", UserId(2) -> "Bob")
println(names(UserId(2)))Validerade newtypes
Kombinera en newtype med en smart konstruktor så att wrappern också upprätthåller invariants. Här kan en NonEmptyString aldrig vara tom.
case class NonEmptyString private (value: String)
object NonEmptyString:
def of(s: String): Option[NonEmptyString] =
if s.nonEmpty then Some(NonEmptyString(s)) else None
object Main:
def main(args: Array[String]): Unit =
println(NonEmptyString.of("hi"))
println(NonEmptyString.of(""))Välj representation
Välj utifrån Era behov.
- case class: enklast, kan användas i mönstermatchning och allokerar ett objekt.
- opaque type: ingen kostnad, ingen boxing, idealisk för prestandakritiska kodvägar och stora samlingar.
object Temp:
opaque type Kelvin = Double
def apply(d: Double): Kelvin = d
extension (k: Kelvin) def value: Double = k
object Main:
def main(args: Array[String]): Unit =
val readings = List(Temp(300.0), Temp(310.5))
println(readings.map(_.value).sum)Förhindra oavsiktliga konverteringar
Två newtypes över samma bastyp konverteras inte implicit. För att gå mellan dem måste Ni skriva en explicit funktion som dokumenterar avsikten.
case class Meters(value: Double)
case class Feet(value: Double)
object Main:
def toFeet(m: Meters): Feet = Feet(m.value * 3.281)
def main(args: Array[String]): Unit =
println(toFeet(Meters(2.0)))När newtypes bör användas
Använd newtypes närhelst en primitiv typ bär på domänbetydelse.
- Identifierare, enheter och formaterade strängar.
- Använd case class för enkelhet och opaque type för prestanda.
- Lägg till en smart konstruktor när det finns invariants.
object Domain:
opaque type Sku = String
def of(s: String): Option[Sku] =
if s.startsWith("SKU-") then Some(s) else None
extension (k: Sku) def raw: String = k
object Main:
def main(args: Array[String]): Unit =
println(Domain.of("SKU-9").map(_.raw))Snabbtest
Testa Er förståelse av newtypes.
Sammanfattning
Ni har lärt Er om newtypes.
- Newtypes kapslar in ett underliggande värde i en separat typ.
- De löser primitive obsession och förhindrar sammanblandning av argument.
- Använd
case classför enkelhet ochopaque typeför noll kostnad. - Lägg till en smart konstruktor för validerade wrappers.
- Konverteringar mellan newtypes är alltid explicita.
object Ids:
opaque type AccountId = Long
def apply(n: Long): AccountId = n
extension (a: AccountId) def value: Long = a
object Main:
def main(args: Array[String]): Unit =
val acc = Ids(1001L)
println(acc.value)Lär dig Scala med en AI-lärare – gratis
Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.
- Kurser
- 39
- Lektioner
- 143
Vanliga frågor
Är lektionen ”Newtypes” gratis?
Ja – hela texten till ”Newtypes” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i Scala för backendutveckling och funktionell programmering, kan Ni uppgradera till CoddyKit PRO. Kursen i Scala för backendutveckling och funktionell programmering innehåller totalt 4 lektioner.
Vad lär jag mig i ”Newtypes”?
Typsäkra omslag Ni övar på Scala för backendutveckling och funktionell programmering med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.
Behöver jag någon erfarenhet för att börja lära mig Scala för backendutveckling och funktionell programmering?
Du behöver inga förkunskaper. Utbildningen i Scala för backendutveckling och funktionell programmering på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 3 av 4.
Hur lång tid tar lektionen ”Newtypes”?
De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.
Kan jag skriva och köra kod i den här Scala för backendutveckling och funktionell programmering-lektionen?
Ja. Varje Scala för backendutveckling och funktionell programmering-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.