Middle
Gdzie używałeś generyków w swoim doświadczeniu programistycznym?
sobes.tech AI
Odpowiedź od AI
Używałem generyków:
- Do implementacji bezpiecznych typowo kolekcji. Na przykład
ArrayList<MyObject>zamiast surowegoArrayList, co zapobiega błędom podczas wykonywania związanych z rzutowaniem typów.// Niebezpieczne podejście (typ surowy) List list = new ArrayList(); list.add("Stringa"); list.add(123); // Dodajemy int // Bezpieczne podejście z generykami List<String> stringList = new ArrayList<>(); stringList.add("Stringa"); // stringList.add(123); // Kompilator zgłosi błąd - Do tworzenia uniwersalnych adapterów RecyclerView. Umożliwia ponowne użycie logiki adaptera dla różnych typów danych.
// Przykład uniwersalnego ViewHolder class GenericViewHolder<T>(itemView: View) : RecyclerView.ViewHolder(itemView) { fun bind(item: T) { // Logika wiązania danych w zależności od typu T } } // Przykład uniwersalnego adaptera class GenericAdapter<T>(private val items: List<T>) : RecyclerView.Adapter<GenericViewHolder<T>>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GenericViewHolder<T> { // Tworzenie ViewHolder } override fun onBindViewHolder(holder: GenericViewHolder<T>, position: Int) { holder.bind(items[position]) } override fun getItemCount() = items.size } - Do tworzenia fabryk obiektów. Aby uzyskać konkretny typ obiektu bez jawnego rzutowania.
interface Factory<T> { T create(); } class StringFactory implements Factory<String> { @Override public String create() { return "Nowy string"; } } class IntegerFactory implements Factory<Integer> { @Override public Integer create() { return 100; } } Factory<String> stringFactory = new StringFactory(); String myString = stringFactory.create(); // Uzyskujemy String bez rzutowania - Do ograniczania typów w metodach i klasach. Użycie ograniczonych generyków (
<T extends MyInterface>lub<T super MyClass>) w celu zapewnienia, że typ generyczny implementuje określony interfejs lub dziedziczy po określonej klasie.// Metoda przyjmująca listę elementów implementujących Serializable public <T extends Serializable> void saveObjectList(List<T> list) { // Logika zapisywania } - W komponentach architektonicznych. Na przykład, podczas pracy z LiveData lub ViewModel, często używa się generyków do określenia typu danych, którymi zarządzają.
// LiveData dla listy obiektów określonego typu val myData: LiveData<List<MyItem>> = // inicjalizacja // ViewModel z danymi określonego typu class MyViewModel : ViewModel() { val data: MutableLiveData<MyStateType> by lazy { MutableLiveData<MyStateType>() } } - Podczas tworzenia klientów sieciowych. Do automatycznego deserializowania odpowiedzi serwera na obiekty określonego typu.
// Przykład serwisu Retrofit z generykiem w odpowiedzi interface ApiService { @GET("users") Call<List<User>> getUsers(); @GET("products/{id}") Call<Product> getProduct(@Path("id") String productId); }
Generyki znacznie poprawiły bezpieczeństwo typów, czytelność i możliwość ponownego użycia kodu.