Wzorzec type class
Polimorfizm ad hoc
Wzorzec type class to bezpłatna lekcja Scala for Backend Engineering & Functional Programming na CoddyKit. To lekcja 1 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 jest klasa typów?
Klasa typów to wzorzec pozwalający dodawać zachowanie do typów bez ich modyfikowania. Jest to forma polimorfizmu ad hoc: ta sama operacja działa dla wielu niezwiązanych ze sobą typów, a kompilator wybiera właściwą implementację na podstawie typu.
Wzorzec ten pochodzi z języka Haskell i jest szeroko stosowany w bibliotekach Scala, takich jak Cats.
Rozwiązywany problem
Dziedziczenie wymusza przypisanie zachowania do typu już podczas jego definiowania. Często jednak nie można edytować danego typu, ponieważ znajduje się on w bibliotece, albo potrzebne są różne zachowania w różnych kontekstach. Klasy typów pozwalają dołączać zachowanie zewnętrznie.
Krok 1: Zdefiniuj trait
Klasa typów to parametrizowany typem trait. W tym przypadku Show[A] opisuje sposób renderowania dowolnego typu A jako String.
trait Show[A] {
def show(value: A): String
}
@main def run(): Unit = {
println("Show trait defined")
}Krok 2: Udostępnij instancje
Dla każdego obsługiwanego typu konkretnego utwórz instance tego traitu. Są to instancje klasy typów.
trait Show[A] { def show(value: A): String }
object Main {
val intShow: Show[Int] = (v: Int) => s"Int($v)"
val strShow: Show[String] = (v: String) => s"Str($v)"
def main(args: Array[String]): Unit = {
println(intShow.show(7))
println(strShow.show("hi"))
}
}Krok 3: Użyj instancji
Funkcja przyjmuje instancję jako parametr. Ta sama funkcja render działa dla każdego typu, który ma instancję Show.
trait Show[A] { def show(value: A): String }
object Main {
def render[A](value: A, s: Show[A]): String = s.show(value)
val intShow: Show[Int] = (v: Int) => s"<$v>"
def main(args: Array[String]): Unit = {
println(render(99, intShow))
}
}Uczynienie parametru niejawnym
Ręczne przekazywanie instancji jest uciążliwe. Oznaczenie instancji i parametru jako implicit (lub użycie using w Scala 3) pozwala kompilatorowi dostarczać instancję automatycznie.
trait Show[A] { def show(value: A): String }
object Main {
implicit val intShow: Show[Int] = (v: Int) => s"<$v>"
def render[A](value: A)(implicit s: Show[A]): String = s.show(value)
def main(args: Array[String]): Unit = {
println(render(42))
}
}Polimorfizm ad hoc
Ta sama nazwa funkcji odwołuje się do różnych implementacji na podstawie typu argumentu. Jest to polimorfizm ad hoc, różniący się od polimorfizmu podtypów (dziedziczenia) i polimorfizmu parametrycznego (generyków).
trait Show[A] { def show(value: A): String }
object Main {
implicit val intShow: Show[Int] = (v: Int) => s"int:$v"
implicit val boolShow: Show[Boolean] = (v: Boolean) => s"bool:$v"
def render[A](value: A)(implicit s: Show[A]): String = s.show(value)
def main(args: Array[String]): Unit = {
println(render(5))
println(render(true))
}
}Praca z własnymi typami
Na tym polega prawdziwa siła tego rozwiązania: można w przejrzysty sposób dodawać zachowanie do własnych typów, pozostawiając klasę danych wolną od kwestii formatowania.
trait Show[A] { def show(value: A): String }
case class User(name: String, age: Int)
object Main {
implicit val userShow: Show[User] = (u: User) => s"${u.name} (${u.age})"
def render[A](value: A)(implicit s: Show[A]): String = s.show(value)
def main(args: Array[String]): Unit = {
println(render(User("Ada", 36)))
}
}Klasy typów a interfejsy
Interfejs wiąże zachowanie z definicją typu, natomiast klasa typów rozdziela te elementy.
- Możesz dodać instancję klasy typów do typu, którego nie jesteś właścicielem.
- Możesz mieć wiele instancji dla różnych kontekstów.
- Kompilator wybiera zachowanie w miejscu wywołania.
Trzy elementy
Każda klasa typów składa się z trzech części:
- trait (operacja abstrakcyjna),
- instancje (implementacje dla poszczególnych typów),
- interfejs (funkcje wymagające instancji).
Każdy z tych elementów poznasz w kolejnych lekcjach.
Kompletny mały przykład
Połączenie wszystkich elementów wzorca za pomocą generycznej funkcji korzystającej z niejawnej instancji.
trait Show[A] { def show(value: A): String }
object Main {
implicit val intShow: Show[Int] = (v: Int) => s"#$v"
implicit val strShow: Show[String] = (v: String) => '"' + v + '"'
def printAll[A](xs: List[A])(implicit s: Show[A]): Unit =
xs.foreach(x => println(s.show(x)))
def main(args: Array[String]): Unit = {
printAll(List(1, 2, 3))
printAll(List("a", "b"))
}
}Szybkie sprawdzenie
Sprawdź, jak rozumiesz wzorzec klasy typów.
Podsumowanie
Nauczyłeś się wzorca klasy typów:
- Parametryzowany
traitopisuje operację. - Instancje implementują ją dla poszczególnych typów.
- Parametry
implicitpozwalają kompilatorowi dostarczać instancję. - Zapewnia to polimorfizm ad hoc i pozwala rozszerzać typy, których nie jesteś właścicielem.
Często zadawane pytania
Czy lekcja „Wzorzec type class” jest bezpłatna?
Tak — pełny tekst „Wzorzec type class” 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 „Wzorzec type class”?
Polimorfizm ad hoc Ć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 1 z 4.
Ile czasu zajmuje lekcja „Wzorzec type class”?
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
- Wzorzec type class
- Definiowanie instancji
- Typowe type classy
- Wyprowadzanie type class