Generics - Java’da class, interface yoki methodni ma’lum bir type bilan xavfsiz ishlatish imkonini beradi.
Oddiy qilib aytganda:
Generics yordamida
Listichida faqatString, faqatInteger, faqatUserkabi aniq turdagi ma’lumot saqlashni majburlaymiz.
Generics bo‘limiga quyidagilar kiradi: Generic classes & methods, bounded type parameters, wildcards ? extends, ? super, type erasure.
1. Generics nega kerak?
Avval Java’da collectionlar generic’siz ishlatilgan.
List list = new ArrayList();
list.add("Ali");
list.add(25);
list.add(true);Bu yomon. Chunki bitta ro‘yxat ichida har xil type bor:
String
Integer
BooleanKeyin olishda muammo chiqadi:
String name = (String) list.get(1);Bu yerda list.get(1) aslida 25, ya’ni Integer.
Natijada runtime xato bo‘ladi:
ClassCastException2. Generics bilan xavfsiz yozish
List<String> names = new ArrayList<>();
names.add("Ali");
names.add("Vali");
names.add("Hasan");Endi bu list ichiga faqat String qo‘shiladi.
names.add(25); // compile errorBu yaxshi. Chunki xato dastur ishlaganda emas, compile vaqtida ushlanadi.
3. List<String>dagi <String> nima?
List<String> names = new ArrayList<>();Bu yerda:
Qism | Ma’nosi |
|---|---|
| Interface |
| List ichida saqlanadigan type |
| Real implementation |
| Generic type belgisi |
Ya’ni:
Bu List faqat String saqlaydi.Boshqa misollar:
List<Integer> numbers = new ArrayList<>();
List<User> users = new ArrayList<>();
Set<Long> ids = new HashSet<>();
Map<Long, User> usersById = new HashMap<>();4. Generics bilan cast qilish shart emas
Generic’siz:
List list = new ArrayList();
list.add("Ali");
String name = (String) list.get(0);Generic bilan:
List<String> names = new ArrayList<>();
names.add("Ali");
String name = names.get(0);Bu yerda (String) yozish shart emas. Java o‘zi biladi: bu list ichida String bor.
5. Generics real backend’da qanday ishlatiladi?
Masalan, userlar ro‘yxati:
List<User> users = new ArrayList<>();ID bo‘yicha user topish:
Map<Long, User> usersById = new HashMap<>();Role nomlari:
Set<String> roles = new HashSet<>();Orderlar:
List<Order> orders = new ArrayList<>();Spring Boot’da ham Generics juda ko‘p ishlatiladi:
ResponseEntity<UserDto>
Optional<User>
Page<ProductDto>
List<OrderResponse>
Repository<User, Long>6. Generic class
O‘zimiz generic class yozishimiz mumkin.
Masalan, bitta oddiy box class:
public class Box<T> {
private T value;
public void setValue(T value) {
this.value = value;
}
public T getValue() {
return value;
}
}Bu yerda T - type parameter.
Endi Boxni har xil type bilan ishlatamiz.
Box<String> stringBox = new Box<>();
stringBox.setValue("Hello");
String text = stringBox.getValue();Box<Integer> integerBox = new Box<>();
integerBox.setValue(100);
Integer number = integerBox.getValue();Box<T> degani:
Box ichidagi qiymatning type’i keyin belgilanadi.7. T, E, K, V nimani anglatadi?
Generics’da harflar odatda shunday ishlatiladi:
Harf | Ma’nosi |
|---|---|
| Type |
| Element |
| Key |
| Value |
| Return type |
Misollar:
public class Box<T> {
}public interface List<E> {
}public interface Map<K, V> {
}Map<K, V> degani:
K - key type
V - value typeMasalan:
Map<Long, User> usersById = new HashMap<>();Bu yerda:
K = Long
V = User8. Generic method
Faqat class emas, method ham generic bo‘lishi mumkin.
public class Printer {
public static <T> void print(T value) {
System.out.println(value);
}
}Ishlatish:
Printer.print("Hello");
Printer.print(123);
Printer.print(true);Bu method har xil type qabul qiladi.
8.1 Generic method real misol
public static <T> T first(List<T> list) {
return list.get(0);
}Ishlatish:
List<String> names = List.of("Ali", "Vali");
String firstName = first(names);List<Integer> numbers = List.of(10, 20, 30);
Integer firstNumber = first(numbers);Bu method qaysi type list kelsa, o‘sha type qiymat qaytaradi.
9. Bounded type parameter
Ba’zida generic type’ni cheklash kerak bo‘ladi.
Masalan, faqat Numberdan meros olgan type’lar bilan ishlaymiz:
public class NumberBox<T extends Number> {
private T value;
public NumberBox(T value) {
this.value = value;
}
public double doubleValue() {
return value.doubleValue();
}
}Endi quyidagilar ishlaydi:
NumberBox<Integer> intBox = new NumberBox<>(10);
NumberBox<Double> doubleBox = new NumberBox<>(15.5);Lekin bu ishlamaydi:
NumberBox<String> stringBox = new NumberBox<>("Hello"); // compile errorChunki String Numberdan meros olmagan.
10. extends generics’da nimani bildiradi?
<T extends Number>Bu degani:
T Number yoki Numberdan meros olgan class bo‘lishi kerak.Misollar:
Integer extends Number
Double extends Number
Long extends Number
Float extends NumberShuning uchun ular qabul qilinadi.
11. Multiple bounds
Bitta type bir nechta shartga ega bo‘lishi mumkin.
public class Repository<T extends BaseEntity & Auditable> {
}Bu degani:
T BaseEntitydan meros olgan bo‘lishi kerak
va Auditable interface’ini implement qilgan bo‘lishi kerak.Muhim qoida:
<T extends ClassName & InterfaceName>Class birinchi yoziladi, interface keyin yoziladi.
12. Wildcard ?
? - noma’lum type degani.
List<?> listBu degani:
Har qanday type’dagi List bo‘lishi mumkin.Misol:
public static void printList(List<?> list) {
for (Object item : list) {
System.out.println(item);
}
}Ishlatish:
printList(List.of("Ali", "Vali"));
printList(List.of(1, 2, 3));
printList(List.of(true, false));Bu method har qanday listni print qiladi.
13. ? extends
List<? extends Number>Bu degani:
Number yoki Numberdan meros olgan type’dagi List.Masalan:
List<Integer>
List<Double>
List<Long>hammasi mos keladi.
Misol:
public static double sum(List<? extends Number> numbers) {
double result = 0;
for (Number number : numbers) {
result += number.doubleValue();
}
return result;
}Ishlatish:
List<Integer> integers = List.of(1, 2, 3);
List<Double> doubles = List.of(1.5, 2.5, 3.5);
System.out.println(sum(integers));
System.out.println(sum(doubles));Bu yerda ? extends Number sababli method Integer, Double, Long listlarini qabul qiladi.
13.1 ? extendsga element qo‘shish muammosi
List<? extends Number> numbers = new ArrayList<Integer>();
numbers.add(10); // compile errorNega?
Chunki Java aniq bilmaydi: bu list aslida Integermi, Doublemi, Longmi?
Masalan:
List<? extends Number> numbers = new ArrayList<Double>();Agar Java numbers.add(10)ga ruxsat bersa, Double list ichiga Integer tushib qolishi mumkin.
Shuning uchun:
? extendsodatda o‘qish uchun ishlatiladi.
14. ? super
List<? super Integer>Bu degani:
Integer yoki Integerning parent type’i bo‘lgan List.Mos keladiganlar:
List<Integer>
List<Number>
List<Object>Misol:
public static void addNumbers(List<? super Integer> numbers) {
numbers.add(10);
numbers.add(20);
numbers.add(30);
}Ishlatish:
List<Integer> integers = new ArrayList<>();
addNumbers(integers);
List<Number> numbers = new ArrayList<>();
addNumbers(numbers);
List<Object> objects = new ArrayList<>();
addNumbers(objects);Bu ishlaydi.
14.1 ? superdan o‘qish
List<? super Integer> numbers = new ArrayList<Number>();
Object value = numbers.get(0);Bu yerda Java faqat Object deb kafolat bera oladi. Chunki list Integer, Number yoki Object bo‘lishi mumkin.
Shuning uchun:
? superodatda yozish uchun ishlatiladi.
15. PECS qoidasi
Generics’dagi eng mashhur qoida:
PECS = Producer Extends, Consumer SuperYa’ni:
Holat | Ishlatiladi |
|---|---|
Collectiondan o‘qisangiz |
|
Collectionga yozsangiz |
|
Oddiyroq:
extends = olish / read
super = qo‘shish / writeMisol:
public static double sum(List<? extends Number> numbers) {
double result = 0;
for (Number number : numbers) {
result += number.doubleValue();
}
return result;
}Bu yerda listdan qiymat olinmoqda. Shuning uchun extends.
public static void addIntegers(List<? super Integer> numbers) {
numbers.add(1);
numbers.add(2);
}Bu yerda listga qiymat qo‘shilmoqda. Shuning uchun super.
16. Generic interface
Interface ham generic bo‘lishi mumkin.
public interface Repository<T, ID> {
T findById(ID id);
void save(T entity);
void deleteById(ID id);
}User uchun:
public class UserRepository implements Repository<User, Long> {
@Override
public User findById(Long id) {
return null;
}
@Override
public void save(User entity) {
}
@Override
public void deleteById(Long id) {
}
}Product uchun:
public class ProductRepository implements Repository<Product, Long> {
@Override
public Product findById(Long id) {
return null;
}
@Override
public void save(Product entity) {
}
@Override
public void deleteById(Long id) {
}
}Bu Spring Data JPA’dagi repositoryga juda o‘xshaydi.
Masalan:
public interface UserRepository extends JpaRepository<User, Long> {
}Bu yerda:
User - entity type
Long - ID type17. Type erasure
Type erasure - Java’da generic type ma’lumotlari compile’dan keyin o‘chiriladi.
Masalan:
List<String> names = new ArrayList<>();
List<Integer> numbers = new ArrayList<>();Compile’dan keyin JVM uchun ular taxminan shunday ko‘rinadi:
List names = new ArrayList();
List numbers = new ArrayList();Ya’ni JVM runtime’da List<String> va List<Integer>ni alohida type sifatida saqlamaydi.
17.1 Type erasure oqibatlari
Quyidagi kod ishlamaydi:
if (names instanceof List<String>) {
}Chunki runtime’da List<String> degan aniq ma’lumot yo‘q.
To‘g‘ri variant:
if (names instanceof List<?>) {
}17.2 Generic array yaratib bo‘lmaydi
Bu ishlamaydi:
List<String>[] lists = new List<String>[10]; // compile errorYoki:
T[] array = new T[10]; // compile errorSababi yana type erasure.
18. Raw type
Raw type - generic classni type bermasdan ishlatish.
Yomon:
List list = new ArrayList();Yaxshi:
List<String> list = new ArrayList<>();Yomon:
Map map = new HashMap();Yaxshi:
Map<Long, User> map = new HashMap<>();Junior uchun qoida:
Raw type ishlatmang. Har doim generic type ko‘rsating.
19. Diamond operator
Java 7'dan boshlab o‘ng tomonda type’ni qayta yozish shart emas.
Eski usul:
List<String> names = new ArrayList<String>();Yangi usul:
List<String> names = new ArrayList<>();Bu <> - diamond operator deyiladi.
Java chap tomondan type’ni o‘zi tushunadi.
20. Optional<T>
Optional ham generic class.
Optional<User> user = findUserById(1L);Bu degani:
Natija User bo‘lishi ham mumkin, bo‘lmasligi ham mumkin.Misol:
public Optional<User> findUserById(Long id) {
if (id == 1L) {
return Optional.of(new User("Ali"));
}
return Optional.empty();
}Ishlatish:
Optional<User> optionalUser = findUserById(1L);
optionalUser.ifPresent(user -> {
System.out.println(user.getName());
});Bu null bilan ishlashni xavfsizroq qiladi.
21. Generic DTO response
Real backend’da umumiy response class yoziladi.
public class ApiResponse<T> {
private boolean success;
private String message;
private T data;
public ApiResponse(boolean success, String message, T data) {
this.success = success;
this.message = message;
this.data = data;
}
public boolean isSuccess() {
return success;
}
public String getMessage() {
return message;
}
public T getData() {
return data;
}
}User qaytarish:
ApiResponse<UserDto> response =
new ApiResponse<>(true, "User topildi", userDto);Product qaytarish:
ApiResponse<ProductDto> response =
new ApiResponse<>(true, "Product topildi", productDto);List qaytarish:
ApiResponse<List<ProductDto>> response =
new ApiResponse<>(true, "Productlar topildi", products);Bu juda foydali. Chunki bitta response class har xil data bilan ishlaydi.
22. Generic service misol
public interface CrudService<T, ID> {
T findById(ID id);
List<T> findAll();
T save(T entity);
void deleteById(ID id);
}User service:
public class UserService implements CrudService<User, Long> {
@Override
public User findById(Long id) {
return null;
}
@Override
public List<User> findAll() {
return List.of();
}
@Override
public User save(User entity) {
return entity;
}
@Override
public void deleteById(Long id) {
}
}Bu yondashuv orqali umumiy CRUD qoidalarini bitta interface’da ushlab turamiz.
23. Generics’da inheritance muammosi
Ko‘pchilik juniorlar shu joyda adashadi.
Integer number = 10;
Number n = number;Bu ishlaydi. Chunki Integer - Numberning child classi.
Lekin:
List<Integer> integers = new ArrayList<>();
List<Number> numbers = integers; // compile errorNega?
Chunki agar bunga ruxsat berilsa, quyidagi muammo chiqadi:
List<Integer> integers = new ArrayList<>();
List<Number> numbers = integers;
numbers.add(10.5); // Double qo‘shildi
Integer value = integers.get(0); // muammoList<Integer> ichiga Double tushib qoladi. Shuning uchun Java buni taqiqlaydi.
To‘g‘ri yondashuv:
List<? extends Number> numbers = integers;Lekin bu holatda listga yangi element qo‘sha olmaysiz.
24. Method parameterlarda Generics
Yomon:
public void printUsers(List users) {
}Yaxshi:
public void printUsers(List<User> users) {
}Yanada umumiy:
public void printAll(List<?> items) {
for (Object item : items) {
System.out.println(item);
}
}Faqat numberlar uchun:
public void printNumbers(List<? extends Number> numbers) {
for (Number number : numbers) {
System.out.println(number);
}
}25. Generics bilan eng ko‘p uchraydigan xatolar
Xato 1: Raw type ishlatish
List list = new ArrayList();To‘g‘ri:
List<String> list = new ArrayList<>();Xato 2: Generic type’ni noto‘g‘ri tushunish
List<Object> objects = new ArrayList<String>(); // xatoTo‘g‘riroq:
List<?> objects = new ArrayList<String>();Xato 3: extendsga element qo‘shishga urinish
List<? extends Number> numbers = new ArrayList<Integer>();
numbers.add(10); // xatoextends - asosan o‘qish uchun.
Xato 4: superdan aniq type o‘qishga urinish
List<? super Integer> numbers = new ArrayList<Number>();
Integer value = numbers.get(0); // xatoTo‘g‘ri:
Object value = numbers.get(0);Xato 5: Generic array yaratish
T[] array = new T[10]; // xatoKo‘pincha uning o‘rniga List<T> ishlatiladi.
26. Junior uchun eng muhim qismlar
Generics’da Junior darajada quyidagilarni yaxshi bilish kerak:
Mavzu | Bilish darajasi |
|---|---|
| Majburiy |
| Majburiy |
Generic class | Kerak |
Generic method | Kerak |
| Kerak |
| Kerak |
| Kerak |
| Kerak |
PECS | Juda foydali |
Type erasure | Asosiy tushuncha yetadi |
27. Amaliy mashq
Vazifa 1: Generic Box
Quyidagi classni yozing:
public class Box<T> {
private T value;
public void set(T value) {
this.value = value;
}
public T get() {
return value;
}
}Ishlatib ko‘ring:
Box<String> stringBox = new Box<>();
stringBox.set("Hello");
Box<Integer> intBox = new Box<>();
intBox.set(100);Vazifa 2: Generic method
public static <T> T getFirst(List<T> list) {
return list.get(0);
}Quyidagilar bilan test qiling:
List<String> names = List.of("Ali", "Vali");
List<Integer> numbers = List.of(10, 20);Vazifa 3: Number sum
public static double sum(List<? extends Number> numbers) {
double result = 0;
for (Number number : numbers) {
result += number.doubleValue();
}
return result;
}Quyidagilar bilan ishlating:
sum(List.of(1, 2, 3));
sum(List.of(1.5, 2.5, 3.5));Vazifa 4: Generic ApiResponse
public class ApiResponse<T> {
private boolean success;
private String message;
private T data;
}Quyidagi variantlar bilan ishlating:
ApiResponse<String>
ApiResponse<User>
ApiResponse<List<User>>Xulosa
Generics Java’da type xavfsizligini oshiradi.
Asosiy eslab qolish kerak:
List<String> names
Map<Long, User> users
Optional<User> user
ApiResponse<ProductDto> responseGenerics sizga:
noto‘g‘ri type qo‘shishni oldini oladi;
cast qilishni kamaytiradi;
runtime xatolarni compile vaqtida ushlaydi;
umumiy class va methodlar yozishga yordam beradi;
Spring Boot, JPA, REST response, service layerlarda juda ko‘p ishlatiladi.