Java Generics được thêm vào ngôn ngữ lập trình Java từ Java 5. Generics trong java là một cách để xác định các kiểu cụ thể cho các lớp và phương thức trong ngữ cảnh khác nhau. Nghe có vẻ hơi trừu tượng, vì vậy chúng ta sẽ xem xét lần lượt các khái niệm và một số ví dụ cụ thể.
Cụ thể trong bài này chúng ta sẽ học:
- Lớp Generic trong java
- Phương thức Generic trong java
- Ký tự đại diện (?) trong Java Generics
- Sử dụng Generics trong các đối tượng Collection
- Lợi thế của Generics trong Java
Nội dung chính
1. Lớp Generic trong java
Một lớp có thể tham chiếu bất kỳ kiểu đối tượng nào được gọi là lớp generic.
Ví dụ
Hãy xem ví dụ đơn giản sau về việc tạo và sử dụng lớp generic:
1. Tạo lớp generic MyGeneric.java:
package vn.viettuts.generics; class MyGeneric<T> { T obj; void add(T obj) { this.obj = obj; } T get() { return obj; } }
Trong lớp trên, tham số <T> là một kiểu chung chung (bạn có thể sử dụng một ký tự bất kỳ khác T) đại diện cho bất kỳ một kiểu cụ nào.
Ví dụ khi bạn định nghĩa MyGeneric<Integer> tức là T = Integer, lúc này bạn có thể hình dung lớp MyGeneric có dạng như sau:
package vn.viettuts.generics; class MyGeneric { Integer obj; void add(Integer obj) { this.obj = obj; } Integer get() { return obj; } }
2. Tạo lớp TestGenerics3.java
package vn.viettuts.generics; public class TestGenerics3 { public static void main(String args[]) { // use Integer MyGeneric<Integer> m1 = new MyGeneric<Integer>(); m1.add(2); System.out.println(m1.get()); // use String MyGeneric<String> m2 = new MyGeneric<String>(); m2.add("Hello"); System.out.println(m2.get()); } }
Trong lớp trên, tại dòng lệnh thứ 6 chúng ta khai báo như sau MyGeneric<Integer>, có nghĩa là kiểu chung T sẽ là Integer. Tại dòng lệnh thứ 11 chúng ta khai báo như sau MyGeneric<String>, có nghĩa là kiểu chung T sẽ là String.
Kết quả:
2 Hello
Quy ước đặt tên tham số
Việc đặt tên tham số là rất quan trọng để học genericis. Các tham số thông thường như sau:
- T - Type
- E - Element
- K - Key
- N - Number
- V - Value
2. Phương thức Generic trong java
Giống như lớp generic, chúng ta có thể tạo phương thức generic có thể chấp nhận bất kỳ kiểu đối số nào.
Chúng ta hãy xem một ví dụ đơn giản về phương thức generic trong java để in các phần tử của mảng. Chúng ta sử dụng E để biểu thị phần tử.
package vn.viettuts.generics; public class TestGenerics4 { public static <E> void printArray(E[] elements) { for (E element : elements) { System.out.print(element + " "); } System.out.println(); } public static void main(String args[]) { Integer[] intArray = { 10, 20, 30, 40, 50 }; Character[] charArray = { 'J', 'A', 'V', 'A' }; System.out.print("In mảng số nguyên: "); printArray(intArray); System.out.print("In mảng ký tự: "); printArray(charArray); } }
Kết quả:
In mảng số nguyên: 10 20 30 40 50 In mảng ký tự: J A V A
3. Ký tự đại diện trong Java Generics
Ký tự ? (dấu chấm hỏi) tượng trưng cho phần tử ký tự đại diện. Nó có nghĩa là bất kỳ loại nào. Nếu chúng ta viết <? mở rộng Number>, nó có nghĩa là bất kỳ lớp con của Number ví dụ như Integer, Float, Double vv. Bây giờ chúng ta có thể gọi phương thức của lớp Number qua bất kỳ đối tượng lớp con nào.
Hãy hiểu nó bằng ví dụ dưới đây:
package vn.viettuts.generics; import java.util.ArrayList; import java.util.List; abstract class Shape { abstract void draw(); } class Rectangle extends Shape { void draw() { System.out.println("ve hinh chu nhat."); } } class Circle extends Shape { void draw() { System.out.println("ve hinh tron."); } } public class GenericTest5 { // tao phuong thuc chi chap nhan tham so la lop con cua Shape public static void drawShapes(List<? extends Shape> lists) { for (Shape s : lists) { s.draw();// goi phuong thuc cua lop Shape boi lop con } } public static void main(String args[]) { List<Rectangle> list1 = new ArrayList<Rectangle>(); list1.add(new Rectangle()); List<Circle> list2 = new ArrayList<Circle>(); list2.add(new Circle()); list2.add(new Circle()); drawShapes(list1); drawShapes(list2); } }
Kết quả:
ve hinh chu nhat. ve hinh tron. ve hinh tron.
4. Sử dụng Generics trong các đối tượng Collection
Trước Generics, chúng ta có thể lưu trữ bất kỳ loại đối tượng nào trong collection như Non-generic. Với generics, được các lập trình java áp dụng để lưu trữ các kiểu cụ thể của các đối tượng.
Cú pháp sử dụng generic collection
ClassOrInterface<Type>
Ví dụ sử dụng Generics trong java:
ArrayList<String>
Ví dụ đầy đủ về Generics trong java
Ở đây, chúng ta sử dụng lớp ArrayList, nhưng bạn có thể sử dụng bất kỳ lớp Collection khác như ArrayList, LinkedList, HashSet, TreeSet, HashMap, Comparator, vv
package vn.viettuts.generics; import java.util.ArrayList; import java.util.Iterator; class TestGenerics1 { public static void main(String args[]) { ArrayList<String> list = new ArrayList<String>(); list.add("Java"); list.add("PHP"); list.add("C++"); // list.add(32);//compile time error // show list Iterator<String> itr = list.iterator(); while (itr.hasNext()) { System.out.println(itr.next()); } } }
Kết quả:
Java PHP C++
Ví dụ về Generics trong java sử dụng Map
Bây giờ chúng ta sẽ sử dụng Map để lưu trữ dữ liệu bằng cách sử dụng generics. Ở đây, chúng ta cần phải truyền key và value:
package vn.viettuts.generics; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; class TestGenerics2 { public static void main(String args[]) { Map<Integer, String> map = new HashMap<Integer, String>(); map.put(1, "Java"); map.put(4, "PHP"); map.put(2, "C++"); // Now use Map.Entry for Set and Iterator Set<Map.Entry<Integer, String>> set = map.entrySet(); Iterator<Map.Entry<Integer, String>> itr = set.iterator(); while (itr.hasNext()) { Map.Entry e = itr.next(); // no need to typecast System.out.println(e.getKey() + " " + e.getValue()); } } }
Kết quả:
1 Java 2 C++ 4 PHP
5. Lợi thế của Generics trong Java
Có 3 ưu điểm chính của generics trong java, như sau:
1. Kiểu đối tượng an toàn: Chúng ta chỉ có thể lưu một kiểu đối tượng duy nhất trong generics. Nó không cho phép lưu trữ 2 đối tượng có kiểu khác nhau.
2. Không cần phải ép kiểu:
Trước Generics chúng ta cần phải ép kiểu, ví dụ:
List list = new ArrayList(); list.add("hello"); String s = (String) list.get(0);// ép kiểu
Sau Generics chúng ta không cần phải ép kiểu đối tượng, ví dụ:
List<String> list = new ArrayList<String>(); list.add("hello"); String s = list.get(0);
3. Kiểm tra lúc biên dịch: nó kiểu tra lỗi khi biên dịch nên sẽ không bị lỗi lúc runtime.
List<String> list = new ArrayList<String>(); list.add("hello"); list.add(32);// Compile Time Error