Generic trong java (Bài 6)
Generic trong java sẽ bắt đầu với khái niệm Generics. Thuật ngữ “Generics” nghĩa là tham số hóa kiểu dữ liệu. Tham số hóa kiểu dữ liệu rất quan trọng vì nó cho phép chúng ta tạo ra và sử dụng một class, interface, method với nhiều kiểu dữ liệu khác nhau.
Một class, interface hay một method mà thực hiện trên một kiểu tham số xác định thì gọi là generic.
Generic trong java – Một số ví dụ
Ví du 1: Tạo class tên SampleGeneric1 trong package generic và sử dụng ArrayList với các kiểu dữ liệu khác nhau
package generic;
import java.util.ArrayList;
/**
*
* @author giasutinhoc.vn
*/
public class SampleGeneric1 {
public static void main(String[] args) {
// Sử dụng ArrayList với các kiểu dữ liệu khác nhau
ArrayList mylist = new ArrayList();
// Thêm vào array
mylist.add(10);
mylist.add("Hello");
mylist.add(true);
mylist.add(15.75);
// Lấy ra
int i = (Integer)mylist.get(0);
String s = (String)mylist.get(1);
boolean b = (boolean)mylist.get(2);
double d = (double)mylist.get(3);
// Hiển thị
System.out.println("Phan tu thu nhat la: " + i);
System.out.println("Phan tu thu hai la: " + s);
System.out.println("Phan tu thu ba la: " + b);
System.out.println("Phan tu thu tu la: " + d);
}
}
Ví dụ 2: Sử dụng ArrayList với các kiểu dữ liệu Integer
Tạo class tên SampleGeneric2 trong package generic
package generic;
import java.util.ArrayList;
/**
*
* @author giasutinhoc.vn
*/
public class SampleGeneric2 {
public static void main(String[] args) {
// Sử dụng ArrayList với các kiểu dữ liệu Integer
ArrayList<Integer> mylist = new ArrayList<Integer>();
// Thêm vào array
mylist.add(10);
mylist.add("Hello"); //Error
// Lấy ra
int i = mylist.get(0);
// Hiển thị
System.out.println("So nguyen: " + i);
}
}
Ví dụ 3: Sử dụng ArrayList với các kiểu dữ liệu String
Tạo class tên SampleGeneric3 trong package generic
package generic;
import java.util.ArrayList;
/**
*
* @author giasutinhoc.vn
*/
public class SampleGeneric3 {
public static void main(String[] args) {
// Sử dụng ArrayList với các kiểu dữ liệu String
ArrayList<String> mylist = new ArrayList<String>();
// Thêm vào array
mylist.add("Nguyen Thi Xuan");
mylist.add("Tran Van Ha");
// Lấy ra
String name1 = mylist.get(0);
String name2 = mylist.get(1);
// Hiển thị
System.out.println("Ho ten nguoi thu nhat: " + name1);
System.out.println("Ho ten nguoi thu hai: " + name2);
}
}
Generic trong java – Ưu điểm của generic
Kiểm tra kiểu dữ liệu trong thời điểm biên dịch
Không cần ép kiểu dữ liệu: đoạn code sau đây không dùng generic nên phải ép kiểu
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0); //phải ép kiểu
Khi dùng generic, việc ép kiểu đã được loại bỏ:
List<String> list = new ArrayList<String>();
list.add("hello");
String s = list.get(0); //không ép kiểu
Cho phép lập trình viên thực hiện các xử lý tổng quát.
Generic trong java – Generic Methods
Là phương pháp giúp tạo ra một phương thức mà có thể được gọi với nhiều kiểu dữ liệu khác nhau. Dựa vào kiểu dữ liệu được truyền vào, trình biên dịch sẽ xử lý mỗi lời gọi phương thức sao cho phù hợp.
Quy ước đặt tên tham số kiểu cho Generics
Ký tự | Ý nghĩa |
E | Element – phần tử |
K | Key – khóa |
V | Value – giá trị |
T | Type – kiểu dữ liệu |
N | Number – số |
Ví dụ tạo class tên GenericMethodTest trong package generic
package generic;
/**
*
* @author giasutinhoc.vn
*/
public class GenericMethodTest {
// generic method printArray
public <T> void printArray(T[] inputArray) {
// Display array elements
for ( T element : inputArray ){
System.out.print(element);
}
System.out.println();
}
public static void main( String args[] ){
// Create arrays of Integer, Double and Character
Integer[] intArray = { 1, 2, 3, 4, 5 };
Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
// Create object
GenericMethodTest gmt = new GenericMethodTest();
System.out.println( "Array integerArray contains:" );
gmt.printArray( intArray ); // pass an Integer array
System.out.println( "\nArray doubleArray contains:" );
gmt.printArray( doubleArray ); // pass a Double array
System.out.println( "\nArray characterArray contains:" );
gmt.printArray( charArray ); // pass a Character array
}
}
Chạy chương trình sẽ cho kết quả như sau:
Generic trong java – Generic Classes
Khai báo một lớp dạng generic giống như khai báo một lớp không phải generic ngoại trừ theo sau tên lớp là một kiểu dữ liệu tổng quát.
Giống như phương thức dạng generic, trong trường hợp có nhiều tham số thì mỗi tham số được phân cách bởi dấu phẩy.
Ví dụ tạo class tên Box trong package generic
package generic;
/**
*
* @author giasutinhoc.vn
*/
public class Box<T> {
private T t;
public void add(T t) {
this.t = t;
}
public T get() {
return t;
}
public static void main(String[] args) {
Box<Integer> intBox = new Box<Integer>();
Box<String> strBox = new Box<String>();
intBox.add(new Integer(10));
strBox.add(new String("Hello World"));
System.out.println("Integer Value: " + intBox.get());
System.out.println("String Value: " + strBox.get());
}
}
Kết quả sau khi chạy chương trình là:
Integer Value :10
String Value :Hello World
Generic trong java – Các ký tự đại diện generic (Wildcards)
Ký tự đại diện <?>
Xét ví dụ sau:
private boolean checkEquals(RestricExample<T> e) {
if(number.doubleValue() == e.number.doubleValue()) {
return true;
}
return false;
}
private boolean checkEquals2(RestricExample<?> e) {
if(number.doubleValue() == e.number.doubleValue()) {
return true;
}
return false;
}
Phương thức checkEquals chỉ chấp nhận tham số e có kiểu dữ liệu giống với biến number. Phương thức checkEquals2 chấp nhận tham số e có kiểu dữ liệu khác với biến number.
Ký tự đại diện <? extends type> chấp nhận bất ký đối tượng nào miễn là đối tượng này kế thừa từ type hoặc đối tượng của type
Ví dụ:
public void processElement(List<? extends A> elements){
...
}
Trong đó:
Sử dụng phương thức processElement (Chấp nhận bất ký đối tượng nào miễn là đối tượng này phải kế thừa từ lớp A hoặc đối tượng của A => ClassA, ClassB và ClassC)
List<A> listA = new ArrayList<A>();
processElement(listA);
List<B> listB = new ArrayList<B>();
processElement(listB);
List<C> listC = new ArrayList<C>();
processElement(listC);
Ký tự đại diện <? super type> chấp nhận bất ký đối tượng nào miễn là đối tượng này là cha của type hoặc đối tượng của type
Ví dụ:
public void processElement(List<? super A> elements){
...
}
List<A> listA = new ArrayList<A>();
processElement(listA);
List<Object> listO = new ArrayList<Object>();
processElement(listO);
Generic trong java – Giới hạn kiểu dữ liệu với ký tự đại diện <? extends type>
Có một số trường hợp chúng ta muốn hạn chế kiểu dữ liệu của các tham số. Chẳng hạn tạo phương thức chỉ chấp nhận tham số với kiểu dữ liệu là số nguyên hoặc số thực.
package generic;
/**
*
* @author giasutinhoc.vn
*/
public class RestricExample <T extends Number> {
private T number;
public RestricExample(T number) {
this.number = number;
}
public double reciprocal() {
return 1/number.doubleValue();
}
public static void main(String[] args) {
RestricExample<Integer> n1 = new RestricExample<Integer>(5);
System.out.println("Reciprocal: " + n1.reciprocal());
RestricExample<Double> n2 = new RestricExample<Double>(7.5);
System.out.println("Reciprocal: " + n2.reciprocal());
//error
//RestricExample<String> s1 = new RestricExample<String>("Hello");
}
}
Xây dựng một phương thức xử lý với nhiều kiểu dữ liệu khác nhau.
Ví dụ tạo class tên MaximumTest trong package generic
package generic;
/**
*
* @author giasutinhoc.vn
*/
public class MaximumTest {
// determines the largest of three Comparable objects
public <T extends Comparable<T>> T maximum(T x, T y, T z) {
T max = x; // assume x is initially the largest
if (y.compareTo(max) > 0) {
max = y; // y is the largest so far
}
if (z.compareTo(max) > 0) {
max = z; // z is the largest now
}
return max; // returns the largest object
}
public static void main(String args[]) {
// Create object
MaximumTest mt = new MaximumTest();
System.out.println("Max of 3, 4, 5 is " + mt.maximum(3, 4, 5));
System.out.println("Maxm of 6.6, 8.8, 7.7 is " + mt.maximum(6.6, 8.8, 7.7));
System.out.println("Max of pear, apple, orange is " + mt.maximum("pear", "apple", "orange"));
}
Kết quả khi sau khi chạy là:
Max of 3, 4, 5 is 5
Max of 6.6, 8.8, 7.7 is 8.8
Max of pear, apple, orange is pear
Một số hạn chế của Generic
Không thể khởi tạo generic với dữ liệu kiểu nguyên thủy
Pair<int, char> p = new Pair<>(8, 'a'); //error
Pair<Integer, Character> p = new Pair<>(8, 'a');
Không thể tạo instance cho kiểu dữ liệu
class Gen<T> {
T obj;
Gen() {
obj= new T(); //Illegal (error)
}
}
Không thể là static trong class
class Gen<T> {
static T obj; //Kiểu T không thể là static
static T getObj() { //Phương thức không thể static
return obj;
}
}
Không thể tạo mảng
Gen<Integer> gens[] = new Gen<Integer>[10]; //error
Gen<?> gens[] = new Gen<?>[10]; //ok
Gens[0] = new Gen<Integer>(25);
Gens[1] = new Gen<String>("Hello");
Không thể tạo class ngoại lệ là generic
Generic trong java – Bài tập thực hành
Bài thực hành số 1: Tạo class tên MyArrayList và thực hiện các công việc sau:
- Thêm vào ArrayList một số nguyên
- Thêm vào ArrayList một số thực
- Thêm vào ArrayList một giá trị boolean
- Thêm vào ArrayList một chuỗi
- In ra màn hình 4 giá trị trên
Bài thực hành số 2: Tạo class tên MyGenericArrayList và thực hiện các công việc sau:
- Tham số hoá dữ liệu cho ArrayList là Integer.
- Sử dụng vòng lặp để nhập các số từ 1 đến 10 vào ArrayList.
- In ra màn hình các giá trị trong ArrayList
Bài thực hành số 3: Tạo class tên Student có các thuộc tính như id, name, age. Viết các phương thức constructor, setter, getter, toString.
Bài thực hành số 4: Tạo class tên Employee có các thuộc tính như id, name, salary. Viết các phương thức constructor, setter, getter, toString.
Bài thực hành số 5: Tạo class PersonModel và thực hiện các công việc sau:
package generic;
import java.util.ArrayList;
/**
*
* @author giasutinhoc.vn
*/
public class PersonModel <T> {
private ArrayList<T> al = new ArrayList<T>();
public void add(T obj) {
al.add(obj);
}
public void display() {
for (T o : al) {
System.out.println(o);
}
}
public static void main(String[] args) {
//Viết xử lý cho phương thức main
}
}
Đoạn code cần viết thêm vào phương thức main để thực hiện các công việc sau:
Tạo đối tượng PersonModel<Student>
- Gọi phương thức add để nhập vào 2 sinh viên
- Gọi phương thức display để hiển thị thông tin của 2 sinh viên vừa nhập
Tạo đối tượng PersonModel<Employee>
- Gọi phương thức add để nhập vào 2 nhân viên
- Gọi phương thức display để hiển thị thông tin của 2 nhân viên vừa nhập
Tạo đối tượng PersonModel<String>
- Gọi phương thức add để nhập vào họ tên của 2 người.
- Gọi phương thức display để hiển thị họ tên vừa nhập.