Sobes.tech
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 surowego ArrayList, 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.