Tóm Tắt
Giới thiệu nội dung bài viết
Generic – tham số hoá kiểu dữ liệu, một thuật ngữ được nhắc đến thường xuyên trong quá trình các bạn học ngôn ngữ lập trình Java. Một trong những lợi ích Generic mang lại cho những lập trình viên Java, đó là cho phép người dùng có thể dễ dàng nhận ra các lỗi với các kiểu dữ liệu không hợp lệ.
Để giúp các bạn hiểu rõ hơn về kiến thức lập trình Java nâng cao này, bài chia sẻ dưới đây anh sẽ giải thích các khái niệm, thuật ngữ, các kí tự dùng trong Generic. Đồng thời hướng dẫn cách để tạo ra Generic Class và Generic Method. Cũng như chỉ ra các ưu, nhược điểm của Generic để giúp các bạn hiểu được khi nào thì nên sử dụng trong lập trình Java.
1. Generic trong lập trình Java là gì
Generic có nghĩa là ta viết các phương thức và lớp để tái sử dụng cho các đối tượng thuộc các kiểu dữ liệu khác nhau (Kiểu dữ liệu như Person , Car , Student, Hotel vv).Nghe có vẻ khó hiểu nên anh sẽ trình bày ví dụ sau đây.
Ví dụ anh muốn viết một chương trình quản lý danh sách học sinh và giáo viên tại trường đào tạo
công nghệ thông tin Ada. Anh sẽ sử dụng List để a lưu lại danh sách của học sinh và giáo viên như sau. Nếu các bạn đã quên về List là gì thì có thể tham khảo lại các loại tập hợp trong lập trình java tại (đây)[https://levunguyen.com/laptrinhjava/2020/04/04/cac-tap-hop-trong-lap-trinh-java/]
- Danh sách học sinh
List
<
Student
>
students
=
new
ArrayList
<
Student
>();
- Danh sách giáo viên
List
<
Teacher
>
teachers
=
new
ArrayList
<
Teacher
>();
Oh, có một điều đặc biệt tại sao List lúc thì chứa đối tượng sinh viên , lúc thì chứa đối tượng là giáo viên. Điều này có kỳ lạ không ?
Bởi vì List được cài đặt theo cách generic nên ta có thể tái sử dụng lại được các kiểu dữ liệu khác nhau (lúc thì chứa sinh viên , lúc thì chứa giáo viên). Sinh viên và giáo viên
là 2 kiểu dữ liệu khác nhau. Do vậy tuỳ vào ngữ cảnh ta truyền vào cho List thì nó có là danh sách sinh viên (List <Student >) hay nó có thể là danh sách giáo viên (List < Teacher >).
Nói cách khác Generic thì ta định nghĩa một kiểu dữ liệu chung chung , và tuỳ vào ngữ cảnh ta truyền vào (Student hay Teacher) thì ta sẽ có tập hợp tương ứng.
2. Cách tạo Generic Class và Generic method
1. Cách tạo Generic Class
Ví dụ ta tạo Generic Class tên là Box. Mọi người chú ý để tạo 1 class là generic ta thêm < T > vào sau class. <T> là ký hiệu của Generic , ta sẽ tìm hiểu ở phần tiếp theo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23public
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
>
integerBox
=
new
Box
<
Integer
>();
Box
<
String
>
stringBox
=
new
Box
<
String
>();
integerBox
.
add
(
new
Integer
(
10
));
stringBox
.
add
(
new
String
(
"Hello World"
));
System
.
out
.
printf
(
"Integer Value :%d\n\n"
,
integerBox
.
get
());
System
.
out
.
printf
(
"String Value :%s\n"
,
stringBox
.
get
());
}
}
Kết quả nhận được sẽ là.
Integer Value :10
String Value :Hello WorldNhư vậy ví dụ trên ta tạo một class Box là generic có 2 phương thức là add và get .Khi ta sử dụng Generic Box trong hàm main ( 58,59) . Tuỳ vào ngữ cảnh mà Generic Box có thể chứa kiểu đối tượng Integer (Box ) hay nó
chứa kiểu dữ liệu là String (Box\<String\>) . Dù kiểu dữ liệu Integer hay String ta đều sử dụng được phương thức get và set đã được định nghỉa trong lớp Generic Box. Như vậy mình thấy
sử dụng Generic mình đỡ phải viết code nhiều. Mình có thể tái sử dụng code cho các đối tượng khác nhau.2. Cách tạo Generic method
Ví dụ ta viết một phương thức in tất cả các phần tử là Generic. Mọi người chú ý tham số truyền vào trong phương thức là chữ <E> đó là tham khi ta muốn viết một hàm generic.
Tuỳ vào tham số truyền vào là kiểu dữ liệu gì . Ta cũng in được các phần tử con trong tập hợp đó
Ví dụ ta viết phương thức printArrayGeneric sau truyền vào tham số là một kiểu generic. Ký tự <E> ta sẽ bàn trong phần tiếp.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27public
class
GenericMethodTest
{
// generic method printArrayGeneric
public
static
<
E
>
void
printArrayGeneric
(
E
[]
inputArrayGeneric
)
{
// Display array elements
for
(
E
elementGeneric
:
ArrayGeneric
)
{
System
.
out
.
printf
(
"%s "
,
elementGeneric
);
}
System
.
out
.
println
();
}
public
static
void
main
(
String
args
[])
{
// Create arrays of Integer, Double and Character
Integer
[]
intArrayGeneric
=
{
2
,
4
,
6
,
8
,
10
};
Double
[]
doubleArrayGeneric
=
{
2.1
,
3.2
,
4.3
,
5.4
};
Character
[]
charArrayGeneric
=
{
'L'
,
'E'
,
'V'
,
'U'
,
'O'
};
System
.
out
.
println
(
"Array intArrayGeneric contains:"
);
printArrayGeneric
(
intArrayGeneric
);
// pass an Integer array
System
.
out
.
println
(
"\nArray doubleArray contains:"
);
printArrayGeneric
(
doubleArrayGeneric
);
// pass a Double array
System
.
out
.
println
(
"\nArray characterArray contains:"
);
printArrayGeneric
(
charArrayGeneric
);
// pass a Character array
}
}
Kết quả nhận được sẽ là.
Array intArrayGeneric contains:
2 4 6 8 10Array doubleArrayGeneric contains:
2.1 3.2 4.3 5.4Array characterArray contains:
L E V U ONhư vậy ở ví dụ trên ta tạo ra một phương thức in ra màn hình là generic . Tuỳ thuộc vào đối số truyền vào là Integer , String, hay Double thì phương thức in đều in ra được các phần tử
Nếu ta truyền đối số là Integer thì sẽ nhận được kết quả là các số nguyên trong tập hợp được in ra . Nếu ta truyền đối số là Double thì ta sẽ nhận được các số thực được in ra . Như vậy ta chỉ viết code 1 lần và sử dụng được cho tất
cả các đối số là những kiểu dữ liệu khác nhau.3. Các ký tự trong Generic
Như ta thấy ở các ví dụ trên ta dùng các ký tự đặt biệt như <T> hay <E> để đặt tên các kiểu dữ liệu và tham số. Ta có thể dùng các từ khác cũng được như X,Y,Z . Nhưng do <T> hay <E>
là các qui ước chung cho các lập trình viên đọc cho dể hiểu, dể bảo trì nên ta không nên đặt các từ khác gây nhầm lẫn. Chúng ta có các qui ước sau.
- E- Element (Phần tử như Student , Teacher)
- K – Key (Giống như key trong tập hợp Map)
- V – Value (V là giá trị giống như kiểu Value trong M )
- N – Number (Kiểu số)
- T – Type (Loại đối tượng ví dụ như con chó , gà , mèo thuộc loại động vật)
4. Generic với các ký tự đại diện
Trong Generic nhiều lúc chúng ta sẽ gặp các ký tự đại diên như : (?),(wildcard), nó đại diện cho một loại dữ liệu không rõ ràng.
- Collection<?>
- List<? extends Number>
- Comparator<? super String>
- Pair<String,?>.
-
Ký tự đại diện <?> chấp nhận tất cả các loại đối số (chứa mọi kiểu đối tượng).
Ví dụ: Collection<?> mô tả một tập hợp chấp nhận tất cả các loại đối số kiểu String, Integer, Boolean, … -
Ký tự đại diện <? extends type>: Các đối tượng bất kỳ nào cũng được nhưng bắt buộc phải có cùng kiểu dữ liệu mới hợp lệ .
Ví dụ: List<? extends Number> mô tả một danh sách, nơi mà các phần tử là kiểu Number hoặc kiểu con của Number. -
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.
5. Generic trong abstract và intefacer
Trong lập trình chúng ta thường sử dụng nhiều generic trong Abstract và Interface để code trở nên gọn hơn tái sử dụng được nhiều lần.
1. Generic trong Abstract được khai báo như sau
1 2 3 4abstract
class
Animal
<
T
>
{
protected
abstract
<
T
>
getAnimalName
();
}
2. Generic trong Interface được khai báo như sau
1 2 3 4 5 6 7public
interface
GenericDao
<
T
>
{
void
insert
(
T
obj
);
void
update
(
T
obj
);
}
6. Lợi ích khi dùng generic
- Kiểu dữ liệu an toàn: Chúng ta chỉ có thể giữ được một loại đối tượng trong Generics. Nó không cho phép lưu trữ các loại đối tượng khác.
- Kiểm tra dữ liệu chặt chẽ ở Compile-time mà không phải là Runtime-error. Nên chúng ta sẽ dễ dàng kiểm soát lỗi hơn.
- Hạn chế việc ép kiểu (cast) thủ công mà không an toàn.
- Giúp chúng ta viết các thuật toán được sử dụng nhiều (reusable), dễ dàng thay đổi, an toàn dữ liệu và dễ đọc hơn.
7. Nhược điểm
- Không thể gọi Generics bằng kiểu dữ liệu nguyên thủy (Primitive type: int, long, double, …), thay vào đó sử dụng các kiểu dữ liệu Object.
- Không thể tạo instances của kiểu dữ liệu Generics.
- Không thể sử dụng static cho Generics.
Và bây giờ, hãy cùng xem code demo ở bên dưới để hiểu rõ hơn nhé .