Sobes.tech
Middle

Czym są generyki (Generics) w programowaniu?

sobes.tech AI

Odpowiedź od AI

Typy generyczne (Generics) to mechanizm w językach programowania, który pozwala tworzyć komponenty (klasy, interfejsy, metody/funkcje) działające z różnymi typami danych, nie tracąc bezpieczeństwa typów i nie korzystając z rzutowania (casting).

Główne cele stosowania generyków:

  • Zwiększenie bezpieczeństwa typów: Kompilator sprawdza typy na etapie kompilacji, wykrywając błędy przed uruchomieniem programu.
  • Poprawa czytelności kodu: Widać wyraźnie, z jakimi typami danych pracuje komponent.
  • Ponowne użycie kodu: Można stworzyć jeden komponent, który działa z różnymi typami, zamiast pisać kilka wersji.
  • Zmniejszenie konieczności rzutowania: Eliminacja konieczności jawnego rzutowania, co czyni kod bardziej czystym i mniej podatnym na błędy ClassCastException (w językach, w których to ma zastosowanie, np. Java).

Przykłady użycia generyków:

  • Kolekcje: Listy (List), zbiory (Set), mapy (Map) mogą przechowywać elementy określonego typu, co gwarantuje, że do kolekcji nie trafią elementy innego typu.
  • Metody/funkcje: Metoda może pracować z parametrami lub zwracać wartość uniwersalnego typu.
  • Klasy/interfejsy: Można tworzyć uniwersalne struktury danych lub wzorce zachowań.

Przykład w Java:

// Klasa generyczna Pair
public class Pair<T, U> {
    private T first;
    private U second;

    public Pair(T first, U second) {
        this.first = first;
        this.second = second;
    }

    public T getFirst() {
        return first;
    }

    public U getSecond() {
        return second;
    }
}

// Użycie
Pair<String, Integer> pair = new Pair<>("Hello", 123);
String str = pair.getFirst(); // Nie trzeba rzutowania
int num = pair.getSecond(); // Nie trzeba rzutowania

// Niepoprawny przykład
// Pair<String, Integer> wrongPair = new Pair<>(123, "Hello"); // Błąd kompilacji: niezgodność typów

Przykład w C#:

// Klasa generyczna Pair
public class Pair<T, U>
{
    public T First { get; set; }
    public U Second { get; set; }

    public Pair(T first, U second)
    {
        First = first;
        Second = second;
    }
}

// Użycie
var pair = new Pair<string, int>("Hello", 123);
string str = pair.First; // Nie trzeba rzutowania
int num = pair.Second; // Nie trzeba rzutowania

// Niepoprawny przykład
// var wrongPair = new Pair<string, int>(123, "Hello"); // Błąd kompilacji: niezgodność typów

Przykład w TypeScript:

// Funkcja generyczna do pobrania pierwszego elementu tablicy
function getFirstElement<T>(arr: T[]): T | undefined {
    if (arr.length > 0) {
        return arr[0];
    }
    return undefined;
}

// Użycie
let numbers: number[] = [1, 2, 3];
let firstNumber = getFirstElement(numbers); // firstNumber ma typ number

let words: string[] = ["a", "b", "c"];
let firstWord = getFirstElement(words); // firstWord ma typ string

// let mixedArray: (number | string)[] = [1, "a", 2];
// let firstMixed = getFirstElement(mixedArray); // firstMixed ma typ number | string

Podsumowując, generics to potężne narzędzie do tworzenia elastycznego, bezpiecznego pod względem typów i wielokrotnego użytku kodu.