Struktury generyczne
Część sekcji Programowanie obiektowe ścieżki GO w Coddy. Lekcja 70 z 107.
Podobnie jak funkcje, struktury również mogą mieć parametry typów. Struktura generyczna pozwala tworzyć struktury danych, które działają z dowolnym typem, zachowując bezpieczeństwo typów. To idealne rozwiązanie do tworzenia wielokrotnego użytku kontenerów, takich jak stosy, kolejki czy pary klucz-wartość.
Aby zdefiniować typ generyczny, umieść parametr typu po nazwie typu:
type Box[T any] struct {
Value T
}
func main() {
intBox := Box[int]{Value: 42}
strBox := Box[string]{Value: "hello"}
fmt.Println(intBox.Value) // 42
fmt.Println(strBox.Value) // cześć
}W przeciwieństwie do funkcji generycznych, podczas tworzenia instancji struktury generycznej musisz jawnie określić typ. Go nie może wywnioskować typu wyłącznie z kontekstu.
Struktury generyczne mogą mieć wiele parametrów typów, dzięki czemu idealnie nadają się do reprezentowania par lub map:
type Pair[K, V any] struct {
Key K
Value V
}
p := Pair[string, int]{Key: "age", Value: 25}
fmt.Printf("%s: %d\n", p.Key, p.Value) // age: 25Możesz także zastosować ograniczenia, aby upewnić się, że przechowywane typy obsługują określone operacje:
type NumberBox[T int | float64] struct {
Value T
}
nb := NumberBox[float64]{Value: 3.14}Struktury generyczne stanowią podstawę do tworzenia bezpiecznych typowo, wielokrotnego użytku struktur danych w Go. W następnej lekcji dowiesz się, jak definiować metody dla tych typów generycznych.
Wyzwanie
ŁatwyZbudujmy uniwersalny system inwentarza, który może śledzić różne typy przedmiotów! Utworzysz wielokrotnego użytku struktury kontenerów, które działają z dowolnym typem, zachowując pełne bezpieczeństwo typów.
Rozmieścisz swój kod w dwóch plikach:
inventory.go: Zdefiniuj swoje generyczne struktury kontenerów.Utwórz generyczną strukturę
Item[T any]z dwoma polami:Name(string) iData(typu T). Reprezentuje ona dowolny przedmiot z powiązanymi danymi elastycznego typu.Utwórz generyczną strukturę
Container[T any]z jednym polemItems, które przechowuje wycinek elementów typuItem[T]. Ten kontener może przechowywać wiele przedmiotów tego samego typu danych.Utwórz generyczną strukturę z ograniczeniem
PricedItem[T int | float64]z trzema polami:Name(string),Quantity(int) iPrice(typu T). Ograniczenie gwarantuje, że ceny zawsze będą liczbami.Zaimplementuj funkcję
NewContainer[T any]() *Container[T], która tworzy i zwraca wskaźnik do pustego Container.Zaimplementuj metodę
Adddla*Container[T], która przyjmuje nazwę (string) i dane (T), tworzy element Item i dodaje go do wycinka Items kontenera.Zaimplementuj metodę
CountdlaContainer[T], która zwraca liczbę przedmiotów w kontenerze.main.go: Pokaż działanie swoich generycznych struktur na różnych typach.Wczytaj typ przedmiotu (
string,intlubpriced), a następnie wczytaj liczbę przedmiotów i ich dane.Dla typu
string: Wczytaj pary składające się z nazwy i danych tekstowych. UtwórzContainer[string], dodaj wszystkie przedmioty, a następnie wypisz każdy przedmiot jako[Name]: [Data], po czym wypisz łączną liczbę przedmiotów.Dla typu
int: Wczytaj pary składające się z nazwy i danych będących liczbą całkowitą. UtwórzContainer[int], dodaj wszystkie przedmioty, a następnie wypisz każdy przedmiot jako[Name]: [Data], po czym wypisz łączną liczbę przedmiotów.Dla typu
priced: Wczytaj trójki składające się z nazwy, ilości i ceny (jako liczby zmiennoprzecinkowej). Utwórz bezpośrednio instancjePricedItem[float64]i wypisz każdą jako[Name] x[Quantity] @ [Price], pokazując cenę z jednym miejscem po przecinku.Wypisz wiersz z liczbą w formacie:
Total items: [count]
Dane wejściowe będą miały następujący format:
- Wiersz 1: Typ przedmiotu (
string,intlubpriced) - Wiersz 2: Liczba przedmiotów (liczba całkowita)
- Kolejne wiersze: Dane przedmiotów zależne od ich typu
Na przykład dla danych:
string
3
Book
Fiction Novel
Pen
Blue Ink
Notebook
Lined PaperWynik powinien wyglądać tak:
Book: Fiction Novel
Pen: Blue Ink
Notebook: Lined Paper
Total items: 3A dla danych:
int
2
Apples
50
Oranges
30Wynik powinien wyglądać tak:
Apples: 50
Oranges: 30
Total items: 2A dla danych:
priced
2
Widget
10
19.99
Gadget
5
49.50Wynik powinien wyglądać tak:
Widget x10 @ 19.9
Gadget x5 @ 49.5
Total items: 2Zwróć uwagę, że ta sama struktura Container działa bez problemu zarówno z ciągami znaków, jak i liczbami całkowitymi, natomiast PricedItem wykorzystuje ograniczenie, aby zapewnić, że jako ceny można stosować wyłącznie typy liczbowe. Podczas tworzenia instancji tych generycznych struktur musisz jawnie podać parametr typu.
Spróbuj swoich sił
package main
import (
"bufio"
"fmt"
"os"
"strconv"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Odczytaj typ elementu
scanner.Scan()
itemType := scanner.Text()
// Odczytaj liczbę elementów
scanner.Scan()
count, _ := strconv.Atoi(scanner.Text())
switch itemType {
case "string":
// TODO: Utwórz Container[string] za pomocą NewContainer
// TODO: Odczytaj 'count' elementów (każdy ma nazwę w jednym wierszu, a dane w następnym)
// TODO: Dodaj każdy element do kontenera
// TODO: Wypisz każdy element w formacie "[Name]: [Data]"
// TODO: Wypisz "Total items: [count]" za pomocą metody Count
case "int":
// TODO: Utwórz Container[int] za pomocą NewContainer
// TODO: Odczytaj 'count' elementów (każdy ma nazwę w jednym wierszu, a dane całkowite w następnym)
// TODO: Dodaj każdy element do kontenera
// TODO: Wypisz każdy element w formacie "[Name]: [Data]"
// TODO: Wypisz "Total items: [count]" za pomocą metody Count
case "priced":
// TODO: Utwórz slice do przechowywania instancji PricedItem[float64]
// TODO: Odczytaj 'count' elementów (każdy ma nazwę, ilość i cenę w osobnych wierszach)
// TODO: Utwórz PricedItem[float64] dla każdego elementu i dodaj go do slice
// TODO: Wypisz każdy element w formacie "[Name] x[Quantity] @ [Price]", wyświetlając cenę z jednym miejscem po przecinku
// TODO: Wypisz "Total items: [count]"
}
_ = scanner // Użyj scanner do odczytu danych wejściowych
_ = count // Użyj count do sterowania pętlą
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy OOP w Go
Pliki zewnętrzneObszar roboczy i moduły GoPakiety i importyNazwy eksportowane i nieeksportowaneWprowadzenie do OOP w GoStruktury jako klasyDefiniowanie metod dla strukturOdbiorniki wskaźnikowe i wartościoweInicjalizacja strukturFunkcje konstruktoraPodsumowanie – prosty kalkulator4Interfejsy
Wprowadzenie do interfejsówImplementacja niejawnaInterfejs jako kontraktPusty interfejs (any)Asercja typuPrzełącznik typówKompozycja interfejsówInterfejsy Stringer i ErrorPowtórka — kalkulator figur7Hermetyzacja
Pola eksportowane i nieeksportowaneHermetyzacja na poziomie pakietuMetody pobierające i ustawiająceUkrywanie informacji w GoPowtórka – dane studentów10Typy generyczne (Go 1.18+)
Wprowadzenie do typów generycznychParametry typówOgraniczenia typówStruktury generyczneObejście ograniczeń metod generycznychPodsumowanie — generyczna kolekcja2Typy i struktury w praktyce
Typy proste i złożoneDefiniowanie własnych typówTagi pól strukturStruktury anonimoweStruktury zagnieżdżoneWartości zerowe i domyślnePodsumowanie – książka kontaktów5Kompozycja zamiast dziedziczenia
Dlaczego Go nie ma dziedziczeniaPodstawy osadzania strukturPromowanie metodOsadzanie wielu strukturOsadzanie a agregacjaPrzesłanianie osadzonych metodPodsumowanie — hierarchia pracowników8Obsługa błędów i programowanie obiektowe
Interfejs błęduWłasne typy błędówOpakowywanie błędów (fmt.Errorf)Błędy wartowniczeerrors.Is() i errors.As()Panic, Defer i RecoverPodsumowanie — parser plików3Wskaźniki i pamięć
Podstawy wskaźników w GoWskaźniki do strukturPrzekazywanie przez wartość a przez referencjęFunkcja new()Odśmiecanie pamięci w GoPowtórka — kreator listy wiązanej6Polimorfizm w Go
Polimorfizm za pomocą interfejsówDuck typing w GoZasady spełniania interfejsówKolekcje polimorficzneWstrzykiwanie zależnościPowtórka — procesor płatności9Współbieżność i OOP
Podstawy goroutinesKanały i komunikacjaKanały buforowane i niebuforowaneInstrukcja selectsync.Mutex i sync.RWMutexsync.WaitGroupProjektowanie struktur bezpiecznych wątkowoPowtórka – pula workerów12Zaawansowane zagadnienia programowania obiektowego
Wzorzec opcji funkcyjnychWzorzec Builder w GoŁańcuchowe wywoływanie metodAliasy typów a definicje typówPodstawy refleksjiPrzegląd generowania koduPoćwicz samodzielnie: Kompilator Go online