Giảng viên: Vũ Duy Linh Email: vdlinh@ctu.edu.vn
135
© Vũ Duy Linh Sự đóng gói
Đây là một trong những đặc điểm quan trọng của OOP.
Phương pháp để thực hiện việc đóng gói là sử dụng:
Phương thức setters, getters: để thiết lập một cơ chế truy xuất đến các thuộc tính riêng của lớp
Các phương thức nội tại chi tiết bên trong
Lợi ích: Việc các đặt, sửa chữa nội dung bên trong lớp không ảnh hưởng đến các lớp khác liên quan
Ví dụ:
1. Khi uống thuốc Aspirin, người dùng có cần biết công thức hóa, cách trị bệnh của nó không?
2. Duyệt web, người dùng có cần biết mã lệnh HTML, CSS, framework, thuật toán,… của chúng là gì không?
3. Xem các ví dụ trong phần thừa kế và đa hình (phía dưới).
136
© Vũ Duy Linh Thừa kế
Thừa kế là một cơ chế cho phép lập trình viên mở rộng một lớp cha cho một lớp con. Trong Java, thừa kế được sử dụng bởi 02 phương cách:
Thừa kế lớp (Class inheritance): Tạo một con lớp mới như là một sự mở rộng (extends) của lớp khác (giúp cho việc tái sử dụng mã lệnh - code) - Java hỗ trợ thừa kế lớp đơn (single class inheritance )
Thừa kế giao diện (Interface inheritance): Tạo một lớp mới để cài đặt (implements) các phương thức đã được định nghĩa trong giao diện. Java hỗ trợ thừa kế bội giao diện - multiple interface inheritance (sẽ được trình bày ở chương Interface).
137
© Vũ Duy Linh Thừa kế lớp (2)
Phương pháp thiết kế:
Tạo mới/sử dụng một lớp cha (tổng quát) không phải là lớp final để định nghĩa những đặc điểm chung của các lớp con (nếu có) Xác định các thành viên (inherited members) của lớp cha có thể được thừa kế cho (các) lớp con.
Tạo (các) lớp con để mở rộng các thuộc tính và phương thức của lớp cha bằng cách thêm vào:
Các thuộc tính, phương thức đặc trưng của lớp con
phương thức trùng tên được gọi là overriding methods, các biến trùng tên được gọi là biến phủ lấp (shadowing variables)
Các private members ở lớp cha không thừa kế cho lớp con, tuy nhiên ta có thể sử dụng phương thức setters/getters để truy xuất các biến riêng ở lớp cha.
Quyền truy xuất ở mức thành viên của các lớp, các lớp con thừa kế ở chung gói, khác gói được thể hiện ở bảng “phạm vi truy cập ở mức thành viên” (slide phía dưới)
Truy xuất các phương thức ở lớp cha thông qua từ khóa super, hoặc phương thức super()
138
© Vũ Duy Linh Thừa kế lớp (3)
Cú pháp định nghĩa lớp con thừa kế:
[modifier] class Subclass extends Superclass { //Class body;
}
Từ khóa extends: là lớp con được mở rộng (dẫn xuất) từ lớp cha nó.
class HinhTron {
private int x, y;
private double bk;
public HinhTron{…}
public setBK(doble bk){…}
protected double dienTich(){…}
}
class HinhTru extends HinhTron { private double chieuCao;
public HinhTru(double chieuCao){
//…
}
// v.v…
}
139
© Vũ Duy Linh Thừa kế lớp (4)
Bảng phạm vi truy cập ở mức thành viên
Quyền truy xuất Phạm vi
public protected default private
Ở cùng lớp Yes Yes Yes Yes
Ở khác lớp và cùng gói Yes Yes Yes No
Ở lớp con và cùng gói Yes Yes Yes No
Ở lớp con và khác gói Yes Yes No No
Ở khác lớp và khác gói Yes No No No
Yêu cầu: Sinh viên viết chương trình hoàn chỉnh để tính thể tích hình trụ theo:
a. Sơ đồ lớp đã thiết kế sẵn
b. Nếu đổi từ protected của dienTich() thành private thì tính được không?
c. Sau khi xong câu a, chương trình có thể coding lại để thay đổi tọa độ và/hoặc kích thước của hình trụ không?
d. v.v…
140
© Vũ Duy Linh Thừa kế lớp (5)
Ví dụ: Giải OOP-PTB2, các lớp chung gói.
141
© Vũ Duy Linh Thừa kế lớp (6)
142
© Vũ Duy Linh Thừa kế lớp (7)
143
© Vũ Duy Linh Mối liên kết - Relationships
Đối tượng kiểu A có một (Has-A) biến thể hiện thuộc kiểu B
Kết tập chặt (Composition): Vòng đời (lifecycle) của đối tượng kiểu B phụ thuộc nội tại vào vòng đời của A A được tạo thì B được tạo (sử dụng final), A hủy thì B bị hủy theo. VD: “Mỗi người có một ngày sinh”,
“Mỗi xe có một động cơ”,…
Kết tập lỏng (Aggregation): Vòng đời của đối tượng kiểu B không phụ thuộc vào vòng đời của A A hủy thì B có thể không bị hủy theo. VD:
“Mỗi TV có một Remote”, “Mỗi laptop có 1 ổ đĩa CD”
[public] class TV{
private Remote itsRmt;
}
[public] class Nguoi{
private final Ngay ngSinh;
public Nguoi(){
ngSinh=new Ngay();
} }
144
© Vũ Duy Linh Mối liên kết (2)
Đối tượng kiểu A có nhiều (* Has-Many) biến thể hiện kiểu B
Liên kết (Association): Vòng đời của đối tượng kiểu B không phụ thuộc vào vòng đời của A. VD: “Mỗi niên giám điện thoại có nhiều số điện thoại”, “Mỗi GV có đăng hoặc không nhiều bài báo khoa học”.
Hỏi: “Mỗi người có tứ chi” hoặc “Mỗi căn nhà có từ một đến 10 phòng”, “Mỗi sinh viên học nhiều GV, mỗi GV dạy nhiều SV”,… thì mối liên kết sẽ ntn?
[public] class BoMon{
private GiangVien soGV[15];
//private Vector<T> soGV; // nếu * }
Theo nguyên lý SOLID, nên ưu tiên xét HAS-A trước khi IS-A
145
© Vũ Duy Linh Mối liên kết (3)
Ví dụ: Demo trên lớp
146
© Vũ Duy Linh Mối liên kết (4)
147
© Vũ Duy Linh Đa hình
Có 3 dạng đa hình (Polymorphism) phân biệt trong ngôn lập trình Java 7:
Nạp chồng phương thức
Ghi chồng phương thức của thừa kế lớp
• Lớp thường (Class)
• Lớp trừu tượng (Abstract class )
• Lớp tổng quát (Generic class)
Ghi chồng phương thức của thừa kế giao diện (Interface)
148
© Vũ Duy Linh Đa hình (2)
Dạng 1 – Nạp chồng (Overloading): Có nhiều phương thức cùng tên ở trong cùng một lớp với số lượng và kiểu tham số khác nhau
Giả sử trong lớp ViDu_Overloading có 04 phương thức:
• Phương thức 1: cong()
• Phương thức 2: cong(int a, int b)
• Phương thức 3: cong(int a, double b)
• Phương thức 4: cong(String a, String b)
Trong phương thức main của lớp ViDu_Overloading, các phương thức được gọi dựa vào đối số nhập vào như sau:
• cong() // phương thức 1 được gọi 0
• cong(2, 4) // phương thức 2 được gọi 6
• cong(5, 6.5) // phthức 3 được gọi 11.5
• cong("OOP" ,“in Java") // phthức 4 được gọi "OOP in Java"
Dạng 2- Ghi chồng (Overriding): Phương thức ở lớp con ghi đè lên phương thức cùng tên ở lớp cha. Nó cho phép một phương thức có nhiều dạng tác động khác nhau trên các loại đối tượng khác nhau.
149
© Vũ Duy Linh Đa hình (3)
Ví dụ 1 - Ghi chồng, nhiều lớp nhiều gói khác nhau.
150
© Vũ Duy Linh Đa hình (4)
151
© Vũ Duy Linh Đa hình (5)
152
© Vũ Duy Linh Đa hình (6)
* Kết quả: Toi la doi tuong hinh tron, co dien tich= 7853.98 Toi la doi tuong hinh cau, co dien tich= 31415.93
153
© Vũ Duy Linh Đa hình (7)
c a s t i n g
Up
Down
Ví dụ:
DongVat dv = new BoSat(); // Upcasting: OK
BoSat bs = (BoSat) dv; // Downcasting: OK (tường minh)
DongVatCoVu dvcv = dv; // Downcasting Error: type mismatch
DongVatCoVu dvcv2 = new DongVatCoVu();
Nguoi nguoi = new Nguoi();
nguoi = (Nguoi) dvcv2; /* Downcasting:
Biên dịch : OK; chạy: ErrorRuntime exception ClassCastException */
Ép (chuyển) kiểu
Ép kiểu lên (Upcasting): đối tượng kiểu lớp con được gán cho đối tượng kiểu lớp cha/tổ tiên đối tượng của lớp con được tham chiếu bởi biến thuộc lớp cha của nó (theo thừa kế phân cấp).
Chuyển kiểu xuống (Downcasting):
Theo thứ tự ngược lại của upcasting và có thể thực hiện bằng ép kiểu tường minh.
154
© Vũ Duy Linh Đa hình (8)
Ví dụ 2: Đa hình thông qua nạp chồng và ghi chồng ở cùng gói
155
© Vũ Duy Linh Đa hình (9)
/**
* @author Vu Duy Linh
* Polymorphism by overriding, overloading */
class LopCha{
protected int x = 10; //not private void hienThi(){
System.out.println("Toi la phuong thuc trong LopCha.");
}
int cong(int a, int b){ return a + b; }
private double cong(int a, double b){ return a + b; } public Double cong(double a, Double b){ return a + b; } String cong(String a, String b){ return a + " " + b; } }
156
© Vũ Duy Linh Đa hình (10)
class LopConA extends LopCha{
//shadow variable – biến bóng: bị che phủ bởi biến cùng tên ở lớp cha int x = 50;
void cong(LopConA lConA1, LopCha lCha ) {//overloading methods }
void cong(LopConA lConA1, LopConA lConA2 ) { } @Override
void hienThi(){ //over-riding method
System.out.println("Toi la phuong thuc trong LopConA."); } }
class LopConB extends LopCha{
int x = 150;
void cong(LopConB lConB, LopConA lConA ) { }
@Override
void hienThi(){ //over-riding method
System.out.println("Toi la phuong thuc trong LopConB."); } }
157
© Vũ Duy Linh Đa hình (11)
public class DaHinh {
public static void main(String[] args) { LopCha lCha = new LopCha();
lCha.hienThi();
System.out.printf("lCha1.x = %d\n\n",lCha.x);
LopCha lCha2 = new LopConA(); //upcasting
lCha2.hienThi(); //Không bị override bởi shadow var System.out.printf("lCha2.x = %d\n\n",lCha2.x);
LopConA lCon = new LopConA();
lCon.hienThi();
System.out.printf("lCon.x = %d\n\n", lCon.x);
/* Error: LopCha khong the convert sang LopCon LopCon lcon2 = new LopCha(); */
158
© Vũ Duy Linh Đa hình (12)
LopCha[] a= new LopCha[3]; a[0]=new LopCha();
a[1]=new LopConA(); a[2]=new LopConB(); //upcasting int so_dtLopCha=0, so_dtLopConA = 0;
for (LopCha lopCha : a) {
lopCha.hienThi(); //run-time <=> late binding if (lopCha instanceof LopCha) so_dtLopCha++;
if (lopCha instanceof LopConA) so_dtLopConA++;
}
System.out.println("So doi tuong \"Is-A\" cua LopCha="
+so_dtLopCha);
System.out.println("So doi tuong \"Is-A\" cua LopConA="
+so_dtLopConA);
} }
Run
Toi la phuong thuc trong LopCha.
lCha1.x = 10
Toi la phuong thuc trong LopConA.
lCha2.x = 10
Toi la phuong thuc trong LopConA.
lCon.x = 50
Toi la phuong thuc trong LopCha.
Toi la phuong thuc trong LopConA.
Toi la phuong thuc trong LopConB.
So doi tuong "Is-A" cua LopCha= 3 So doi tuong "Is-A" cua LopConA= 1