- [Java Core] B1: Tại sao nên học Java, syntax cơ bản
- [Java Core] B2: Class và Object (Lớp và đối tượng)
- [Java Core] B3: Cách Java quản lý dữ liệu
- [Java Core] B4: Tính chất đóng gói, kế thừa và đa hình trong Java
- [Java Core] B5: Tính chất trừu tượng, Interface và Abstract Class trong Java
- [Java Core] B6: Design Pattern Iterator. Iterable và Collection trong Java
- [Java Core] B7: Exception trong Java
Buổi 5: Interface và trừu tượng
- Interface là gì?
- Interface và Abstract class
- Tính trừu tượng
- Khi nào dùng interface, khi nào dùng abstract class?
- Bài tập về interface và trừu tượng
Interface là gì?
- Interface là một khái niệm trừu tượng, nó chỉ ra những gì mà một đối tượng có thể làm, nhưng không nói ra làm thế nào để đối tượng đó làm được điều đó.
Ví dụ cú pháp khai báo interface:
- Trong ví dụ trên, interface Animal chỉ ra rằng một Animal có thể làm được 2 việc là ăn và di chuyển, nhưng không nói ra làm thế nào để ăn và di chuyển.
- Lúc này, ví dụ ta có 1 class
Dog
vàCat
thì ta có thể implement interfaceAnimal
vào 2 class này như sau:
- Lúc này, ta có thể khởi tạo 2 đối tượng
Dog
vàCat
và gọi các phương thứceat()
vàtravel()
mà không cần quan tâm đến cách thức của 2 phương thức này.
- Kết quả khi chạy chương trình:
Dog is eating
Dog is travelling
Cat is eating
Cat is travelling
Take away | Tổng hợp
- Như vậy, interface có thể nhìn nhận như 1 bản hợp đồng, tôi quy định trước, tôi chỉ định nghĩa là, các class implement interface này phải làm được những gì tôi quy định.
- Như vậy, khi bên khác implement interface này, bên đó phải làm theo những gì mà interface quy định, nếu không làm theo, bên đó sẽ bị lỗi compile.
- Còn nếu bên nào sử dụng interface, ta có thể khởi tạo đối tượng của class implement interface đó, và gọi các phương thức, và biết chắc
phương thức tồn tại và có thể gọi được ở class đó
, vìclass đó đã implement interface đó
.
Abstract class
- Abstract Class – Lớp trừu tượng, là 1 lớp trong Java, nhưng có thể chứa cả các phương thức trừu tượng (abstract method) và các phương thức thường (non-abstract method).
- Abstract Class không thể khởi tạo đối tượng, nhưng có thể khai báo biến kiểu Abstract Class, và khởi tạo đối tượng của class con của Abstract Class.
Ví dụ:
- Kết quả khi chạy chương trình:
Dog is eating
Animal is travelling
Cat is eating
Animal is travelling
Take away | Tổng hợp
- Như vậy, Abstract Class nhìn chung giống Class, nhưng mà có thể tạo thêm
phương thức trừu tượng
, các class kế thừa từ Abstract Class này phải implement các phương thức trừu tượng này, nếu không sẽ bị lỗi compile. - Các phương thức trừu tượng này, có thể hiểu là các phương thức mà Abstract Class này quy định, các class kế thừa từ Abstract Class này phải có, và phải implement các phương thức này.
- Các phương thức thường (non-abstract method) thì không cần implement, vì các class kế thừa từ Abstract Class này đã có sẵn các phương thức này.
- Abstract Class thì không thể khởi tạo đối tượng, nhưng có thể khai báo biến kiểu Abstract Class, và khởi tạo đối tượng của class con của Abstract Class.
Ví dụ
- Lí do, vì abstract class hay interface chứa những hàm trừu tượng, những hàm chỉ định nghĩa, chưa có logic và giao việc triển khai logic cho các nơi ghi đè nó, nên không thể khởi tạo đối tượng từ abstract class hay interface được. (Vì nó chưa có logic, chưa có gì để chạy cả)
So sánh Interface và Abstract Class
Interface | Abstract Class |
---|---|
Interface chỉ có thể chứa các phương thức trừu tượng | Abstract Class có thể chứa cả các phương thức trừu tượng và các phương thức thường |
Interface không thể khởi tạo đối tượng | Abstract Class không thể khởi tạo đối tượng |
Biến trong interface mặc định là public static final | Biến trong abstract class không có mặc định gì cả |
Interface không có constructor | Abstract Class có constructor |
Interface không có gì để ghi đè | Abstract Class có thể có các phương thức để ghi đè |
Interface có thể kế thừa nhiều interface khác | Abstract Class chỉ có thể kế thừa 1 abstract class khác |
Các class implement interface có thể implement nhiều interface khác | Các class kế thừa từ abstract class chỉ có thể kế thừa 1 abstract class khác |
- Note: Sau này interface có thể có các phương thức thường, nhưng mà vẫn không thể khởi tạo đối tượng từ interface được.
- Về biến trong interface, mặc định là
public static final
, nên khi khai báo biến trong interface, không cần khai báopublic static final
nữa. Tức là các biến trong interface là tồn tại duy nhất, không đổi được, ở dạng static. - Về biến trong abstract class, không có mặc định gì cả, nên có thể tạo các “biến thường”, tức là mỗi object tạo ra bởi 1 class chứa giá trị khác nhau.
Special Question: Diamond Problem
- Diamond Problem là gì?
- Diamond Problem là một vấn đề xảy ra khi đa kế thừa, tức là nếu 1 class kế thừa từ 2 nơi có trùng thứ gì đó, thì nó làm sao biết nên dùng cái nào ???
- Mặc định thì, Java không cho phép kế thừa từ 2 class, nhưng nếu kế thừa từ 2 interface thì được.
(Vì interface chỉ chứa các phương thức trừu tượng, không có gì để ghi đè)
- Ví dụ:
Khi nào dùng interface, khi nào dùng abstract class?
- Khi nào dùng interface, khi nào dùng abstract class?
- Câu trả lời đơn giản:
Khi nào cần thì dùng, tuỳ vào mục đích của mình
- Nhưng mà, để hiểu rõ hơn, ta cùng xem ví dụ sau:
- Như vậy, ta có thể thấy, khi dùng interface, ta phải implement lại cả phương thức
travel()
, trong khi khi dùng abstract class, ta không cần implement lại phương thứctravel()
. - Tuy nhiên, interface lại cho phép ta
kế thừa nhiều interface khác
, trong khi abstract class thì không.
Tổng kết
- Có thể thấy, abstract class cho phép ta có các khả năng trừu tượng, và có thể chứa các logic có sẵn ta muốn dùng cho nhiều class con. Lúc này ta có thể cân nhắc sử dụng abstract class
- Thay vì điều đó, interface thường được dùng để chỉ
behavior - tính cách, cách xử sự
của 1 đối tượng. Tức là 1 cái ô tô thì có thể xử lý các interface làChayXe, ChayDongCo, ChayDien, ChayDau
… Như vậy, interface thường được dùng để chỉ ratính chất
của 1 đối tượng, còn logic thì để class implement interface đó tự triển khai.
- Với abstract class, ta dùng cho những case ta có logic tập trung, có thể reuse ở class con. Vừa có khả năng trừu tượng mà các class con sẽ có đặc tính riêng. Tuy nhiên nếu class đó quá lớn, ví dụ class PhuongTien có đủ thứ hàm như
ChayXe, ChayDongCo, ChayDien, ChayDau
… , thì ví dụ XeDap kế thừa lại có các hàm chạy động cơ, chạy điện =>Như thế bị thừa, không cần thiết, làm confuse cho người đọc code
=> Ta nên tách ra thành interface. - Với interface, ta dùng cho những case ta chỉ cần chỉ ra
tính chất
của 1 đối tượng, còn logic thì để class implement interface đó tự triển khai.
Tính trừu tượng
- Tính trừu tượng là gì?
- Tính trừu tượng là một khái niệm trong lập trình hướng đối tượng, nó cho phép ta chỉ ra những gì mà một đối tượng có thể làm, nhưng không nói ra làm thế nào để đối tượng đó làm được điều đó.
- Tức là, khi ta dùng xe máy, ta chỉ biết là nó có thể chạy, có thể dừng, có thể bật đèn, có thể bật còi, nhưng ta không cần biết làm thế nào để nó chạy, dừng, bật đèn, bật còi.
- Tính trừu tượng là một khái niệm quan trọng trong lập trình hướng đối tượng, nó cho phép ta tách biệt được phần logic của đối tượng và phần giao tiếp với đối tượng. Ta chỉ quan tâm đến phần giao tiếp với đối tượng, còn phần logic của đối tượng thì ta không cần quan tâm.
Điều này giúp ta có thể dễ dàng thay đổi phần logic của đối tượng mà không ảnh hưởng đến phần giao tiếp với đối tượng.
Tại sao cần sử dụng interface
- Giả sử, ta có 1 interface là “Athlete” – Vận động viên với 2 hàm là play() – là chơi thể thao nào đó
- Giả sử, ta có 1 class Team – 1 đội chơi, sẽ có thể thêm vận động viên vào, ta nhận thấy, nếu có thể, ta luôn lên sử dụng interface thay vì khai báo thẳng 1 class cụ thể, vì khi đó, ta có thể thêm bất cứ vận động viên nào vào đội chơi, miễn là vận động viên đó implement interface Athlete.
- Như code trên, bất kì vận động viên nào implement interface Athlete đều có thể thêm vào đội chơi, và có thể chơi được. Điều này giúp code
dễ mở rộng, flexible
hơn, sau này ta có thể thêm bất cứ vận động viên nào vào đội chơi, miễn là vận động viên đó implement interfaceAthlete
.
Đây cũng được biết là quy tắc prefer interface over class
hay prefer composition over inheritance
Interface làm đúng sự phụ thuộc của module level cao
- Giả sử, ta có class
DienThoai
, khi này khi ta muốn sạc điện thoại, ta sẽ gọi 1 class bên trongDienThoai
là sạc điện bằng củ sạc Samsung
- Ta thấy, với thiết kế hiện tại, ta sẽ truyền tham số là cường độ vào củ sạc Samsung để class đó thực hiện sạc.
- Tuy nhiên, bất kì sự thay đổi nào ở class
CuSac
vào phương thức sac có thể yêu cầu class điện thoại truyền thêm tham số, hay phải dùng tên hàm khác, … - Điều này là không đúng,
điện thoại
rõ ràng là module cấp cao hơn, và nên có quyền quyết định dùng củ sạc nào, chứ không phảicủ sạc
quyết định điện thoại phải làm gì để dùng. - Vì vậy, ta nên tạo ra 1 interface là
CuSac
để điện thoại có thể sạc, và classCuSacSamSung
implement interfaceCuSac
này.
- Lúc này, sự phụ thuộc đã thay đổi. Giờ DienThoai chỉ sử dụng interface củ sạc, ví dụ như Iphone quy định là tôi dùng cổng lightning, máy Samsung bảo tôi dùng cổng sạc USB-C vậy. Các nhà sản xuất củ sạc phải đi theo họ.
- Thì ở đây, DienThoai dùng interface để định nghĩa là tôi sẽ muốn sạc như thế nào. Còn củ sạc thì implement interface đó, và tự triển khai logic riêng của mình, nhưng phải tuân thủ theo hợp đồng interface mà DienThoai sử dụng