You are on page 1of 73

BÀI 3: ĐA HÌNH

TRONG LẬP TRÌNH HƯỚNG ĐỐI TƯỢNG

Hà Thị Kim Dung


Nội dung

Giới thiệu chung Khái niệm đa hình

❖ Nhắc lại một số ý ❖ Thuật ngữ


tưởng về thừa kế Polymorphism
❖Lịch sử tính đa hình
❖Định nghĩa
❖Phân loại đa hình
❖Phân tích một vài ví dụ

2
1. Giới thiệu chung
❖ Nhắc lại một số ý tưởng về thừa kế Đóng gói
Encapsulation

• 3 cột trụ của tam giác P.I.E của OOP:


đóng gói (E), thừa kế (I) và đa hình
P.I.E
(P).
Thừa kế Đa hình
• Nhắc lại về Thừa kế: Inheritance Polymorphism

✓Lớp con: thừa kế thuộc tính và hành vi ✓ Lớp con có thể định nghĩa lại
của lớp cha cộng với các thuộc tính và các phương thức của lớp cha
với điều kiện:
hành vi của riêng nó. o Phải có quyền truy cập không
✓Một lớp cha có nhiều lớp con, nhưng một chặt hơn phương thức được định
nghĩa lại.
lớp con chỉ có một lớp cha.
o Phải có kiểu giá trị trả lại như
✓protected: Cấp quyền truy nhập tới thuộc nhau.
tính/phương thức cho các lớp con (và chỉ o Tái sử dụng phương thức cùng
tên của lớp cơ sở: gọi tường
các lớp con). minh bằng super. 3
1 ❖ Nhắc lại một số ý tưởng về thừa kế (tiếp)
• Thừa kế và đa hình:
Đóng gói
✓Hai đặc trưng có mối liên hệ mật Encapsulation

thiết với nhau:


o Mối liên hệ lớp cha – lớp con
o Thừa kế: liên quan tới các lớp và sự
phân cấp các lớp P.I.E
o Đa hình: liên quan tới các phương
thức của các lớp này. Thừa kế Đa hình
Inheritance Polymorphism
✓Hoàn thiện kiến thức về thừa kế để
tìm hiểu về đa hình.
Đa hình là gì?
Tại sao phải cần có đa hình?
Cài đặt đa hình như thế nào?
1/ 4
2. Một số khái niệm

❖Thuật ngữ Polymorphism:

• Thuật ngữ polymorphism: bắt nguồn từ Hy Lạp


• Polymorphism: poly (nhiều) + morph (hình dạng) = đa hình
• Polymorhism trong sinh học có nghĩa là hiện tượng nhiều
hình dạng.
• Trong hướng đối tượng, sử dụng khái niệm này để chỉ hiện
tượng cùng hành vi nhưng cách thực hiện khác nhau.

❖/ 5
2 ❖ Lịch sử tính đa hình
• Trong lập trình, cần một thứ gì đó để phân biệt một hàm/phương thức
với các hàm/phương thức khác: Khái niệm chữ kí Signature.
• Trong ngôn ngữ lập trình có cấu trúc (C): Dùng tên hàm làm chữ kí. Vì
thế các hàm phải khác tên nhau.
• Sau đó, xây dựng cho ngôn ngữ lập trình hướng đối tượng (Java/C++):
✓ Không dùng tên phương thức làm chữ kí.
✓ Hai hàm trùng tên vẫn được chấp nhận trong một lớp.
✓ Chữ kí: nếu hai phương thức trùng tên thì bắt buộc phải có số lượng, thứ
tự hoặc kiểu của tham số truyền vào cho các phương thức này phải khác
nhau.
✓ Ví dụ:
foo(int i) và foo(int i, int j): hai phương thức khác nhau.
foo(int i) và foo(int k): giống nhau
foo(int i, double d) và foo(double d, int i): khác nhau.
6
2 ❖ Lịch sử tính đa hình (tiếp)
✓ Trong C++: chữ kí còn bao gồm kiểu trả về của một hàm.
✓ Java: không cần điều đó.
• Các phương thức trùng tên tồn tại trong cùng một lớp →tính đa hình.
• Hơn nữa, lớp con có thể cài đặt lại các phương thức của lớp cha→
phương thức ở lớp con được trùng tên với phương thức lớp cha.
➔ Trong lập trình hướng đối tượng, môt hành vi có thể có nhiều cách thể
hiện khác nhau trên các đối tượng khác nhau.
✓ Ví dụ: a+b
▪ Nghĩ là chỉ có thể thực hiện được khi a và b là các số: 2+3 = 5
▪ Với đa hình, còn có thể cộng xâu: "xin"+"chao"="xin chao".
▪ Phép toán + : với số thực hiện một phép tính cộng số học, với xâu kí tự
thực hiện việc nối xâu v.v..

❖/ 7
2 ❖Định nghĩa
• Đa hình là việc cho phép 1 thao tác có thể có những cách thực
hiện khác nhau trên những đối tượng khác nhau
Định nghĩa khác:
• Đa hình là hiện tượng các đối tượng thuộc các lớp khác nhau có
khả năng hiểu cùng một thông điệp theo các cách khác nhau
• Đa hình là một trong những đặc tính quan trọng nhất của lập
trình hướng đối tượng (một trong 3 trụ cột của tam giác hướng
đối tượng):
– Tính sử dụng lại thể hiện rõ ràng nhất.
– Tăng tính linh hoạt, mềm dẻo khi lập trình: sử dụng lại các lớp đã
được định nghĩa nhiều lần mà không cần phải quan tâm xem chúng có
trùng lắp với nhau hay không? Có xung đột với nhau hay không?
– Tiết kiệm bộ nhớ. ❖/ 8
2 ❖Phân loại đa hình
• Có 3 loại đa hình: overloading, parametric, overriding.
– Overloading (nạp chồng). Hàm có thể trùng tên, thực hiện các chức
năng giống nhau trong các lớp độc lập với nhau (có thể các lớp này
không có quan hệ thừa kế).
Ví dụ: Lớp Image của các lớp đối tượng ảnh, lớp Link của các đối tượng
liên kết đều có thể có hàm Display thực hiện chức năng hiển thị đối
tượng lên màn hình.
→Không cần phải lo lắng đến kiểu của đối tượng khi thực hiện thao tác
hiển thị đối tượng lên màn hình.
Đa hình overloading cho định nghĩa một toán tử nào đó có hành vi như
thế nào phụ thuộc vào các toán hạng: phép cộng a +b.

9
2 ❖Phân loại đa hình (tiếp)
– Parametric (truyền tham số): Cho phép các hàm cùng tên, nhưng có
danh sách tham số truyền vào khác nhau (tên, kiểu, thứ tự tham số).
Vì thế, có thể định nghĩa phương thức addition() :
int addition(int, int): tính tổng của 2 số nguyên kiểu int.
float addition(float, float): tính tổng của 2 số thực kiểu float.
char addition(char, char): tính tổng của 2 kí tự, phép tính này sẽ cho kết quả
tùy thuộc vào người lập trình.
Khi gọi tên một hàm, truyền giá trị của các đối số cho hàm, trình biên
dịch có thể chọn được phương thức phù hợp.
→Chữ kí là: tên và kiểu được xác định bởi giá trị của các đối số khi
truyền vào cho hàm.
→ Như vậy, chữ kí của hàm sẽ xác định xem trong một loạt các hàm
trùng tên, hàm nào sẽ được sử dụng. 10
2 ❖Phân loại đa hình (tiếp)
– Overriding (vượt quyền): Khả năng định nghĩa lại một phương thức của
lớp cha tại các lớp con.
Có thể gọi phương thức của một đối tượng mà không phải kiểm tra lại kiểu
Loại đa hình này tạo ra sự khác biệt giữa các lớp trong một họ các đối tượng
(cha khác con, anh em khác nhau) dù chúng có giao diện chung (có thuộc
tính và hành vi thừa kế từ cha).
Ví dụ quân cờ: Các đối tượng King (Tướng), Queen (Hậu), Bishop (Tượng),
Knight (Mã), Rook (Xe), Pawn (Tốt) thừa kế từ Piece (Quân cờ). Hành vi
movement (di chuyển) thừa kế từ Piece, nhưng mỗi quân cờ có cách di
chuyển khác nhau .
Nếu gọi Piece.movement: hoạt động tùy xem đó là quân nào.
• Không phải lúc nào cũng có thể phân biệt được 3 loại trên một cách rõ
ràng. Vì hiện tượng đa hình xảy ra, thường có sự hiện diện của 3 loại
đa hình trên.
❖/ 11
2 ❖Phân tích một vài ví dụ
• Ví dụ 1: Cây thừa kế một số đối tượng hình học
• Ví dụ 2: Trò chơi mua nước giải khát
• Tóm lại: ta liên lạc với các đối tượng bằng các thông
điệp (các lời gọi phương thức) để yêu cầu đối tượng thực
hiện một hành vi nào đó. Việc hiểu các thông điệp đó
như thế nào chính là nền tảng cho tính chất đa hình của
hướng đối tượng.

2/ 12
Tổng kết
• Đa hình là việc cho phép 1 thao tác có thể có những cách thực hiện
khác nhau trên những đối tượng khác nhau
• Đa hình là một trong những đặc tính quan trọng nhất của lập trình
hướng đối tượng (một trong 3 trụ cột của tam giác hướng đối tượng):
✓ Sử dụng lại.
✓ Cho phép một hành vi có nhiều cách thể hiện khác nhau ở các lớp đối
tượng khác nhau.
✓ Tiết kiệm bộ nhớ.
• Có 3 loại đa hình: overloading, parametric, overriding.
✓ Overloading (nạp chồng): nạp chồng toán tử, hàm trùng tên.
✓ Parametric (truyền tham số): hàm cùng tên, nhưng có danh sách
tham số truyền vào khác nhau (tên, kiểu, thứ tự tham số).
✓ Overriding (vượt quyền): Định nghĩa lại một phương thức của lớp
cha tại các lớp con.
13
BÀI 4: CÀI ĐẶT ĐA HÌNH TRONG JAVA

Hà Thị Kim Dung


Nội dung

1 So sánh Overloading và Overriding

2 Đa hình và liên kết phương thức

3 Phương thức riêng

4 Lớp trừu tượng và phương thức trừu tượng

5 Đa thừa kế và Giao diện


1. So sánh Overloading và Overriding
❖ Khái niệm Overloading
• Hai hoặc nhiều hàm/phương thức trùng tên với các chữ kí khác nhau
được gọi là overloading-nạp chồng.
• Dạng đơn giản đầu tiên của đa hình: Đa hình hàm - function
polymorphism hay còn gọi là hàm chồng- function overloading:
✓ Hàm trùng tên nhưng khác nhau về danh sách tham số.
✓ Ví dụ: foo(int i); và foo(int i, int j); khai báo trong cùng 1 file (C++)
• Phương thức chồng - method overloading cũng tương tự: hai phương
thức trùng tên nhưng danh sách tham số khác nhau.

16
1 ❖ Khái niệm Overloading (tiếp)
• Ví dụ 1: Phương thức chồng
class Test {
public static void main(String args[]) {
myPrint(5);
myPrint(5.0);
}
public void myPrint(int i) {
System.out.println("int i = " + i);
}
public void myPrint(double d) {
System.out.println("double d = " + d);
}
}

❖/ 17
1 ❖Phân tích Overloading
• Các khả năng mà Overloading mang lại:
✓ Hai lớp độc lập với nhau có phương thức trùng tên nhau, thậm chí cả
về kiểu và danh sách tham số. Ví dụ: 2 lớp Image và Link cùng có
phương thức Display: void Display(){....}
✓ Tập trung mô tả trong một lớp:
o Mọi phương thức đều có thể nạp chồng kể cả constructor
✓ Lợi ích mang lại:
o Làm cùng một việc với nhiều kiểu dữ liệu khác nhau
o Chia một việc lớn thành nhiều việc nhỏ, mỗi việc do 1 phương thức đảm
nhiệm.

18
1 ❖Phân tích Overloading (tiếp)
• Khi nạp chồng, có thể đứng trong phương thức này gọi tới
phương thức nạp chồng với nó
• Ví dụ 2:
✓ Trong định nghĩa phương thức
int increment(int amount) { increment() có một lời gọi hàm tới
count = count + amount;
return count; phương thức increment(int
} amount).
int increment() { ✓ Trình biên dịch vẫn hiểu đây là
return increment(1); hai phương thức khác nhau, chứ
} không phải gọi đệ quy.

• Ứng dụng: cung cấp nhiều thông tin gộp lại từ nhiều nguồn khác
nhau.
19
1 ❖Phân tích Overloading (tiếp)
• Ví dụ 3: cung cấp nhiều thông tin gộp lại từ nhiều nguồn khác nhau
void getInfo(String name, String date, String address ) {
// in tên, ngày sinh, quê quán
System.out.println(“Ho ten:”+name+” Ngay sinh:”+date+” Dia
chi:”+address);
}
void getInfo(String class, String faculty) {
// cung cấp lớp học, ngành học
System.out.println(“Lop:”+class+” Khoa:”+faculty);
}
void getInfo( Person a, School s, String message) {
System.out.println(message + ": ");
getInfo(a.getname(), a.getdate(), a.getaddress());
getInfo(s.getclass(), s.getfaculty());
}
20
1 ❖Phân tích Overloading (tiếp)
• Nạp chồng constructor:
– Cũng tương tự như nạp chồng phương thức.
– Tuy nhiên cũng có một vài lưu ý
– Ví dụ 4:
– Phương thức khởi tạo thứ 2 sử dụng từ khóa this.
Point(int x, int y) ✓ Từ khóa này là một tham số ẩn của phương thức
{ mang ý nghĩa rằng đối tượng đang được khởi tạo.
this.x = x;
✓ this(0,0) sẽ gọi đến phương thức khởi tạo point(int
this.y = y;
sum = x + y; x, int y)
} ✓ lời gọi phương thức: this(0,0) luôn phải nằm ở câu
Point() { lệnh đầu tiên của phương thức khởi tạo
this(0, 0); – Ứng dụng: cung cấp các giá trị mặc định cho các
….
}
tham số bị mất.
✓ point(): Mỗi điểm được khởi tạo bằng point(), sẽ
mặc định cung cấp tọa độ là (0,0) 21
1 ❖Phân tích Overloading (tiếp)
• Ứng dụng tiếp theo: làm cùng
một việc với nhiều kiểu dữ liệu class Student extends Person {
...
khác nhau. void printInformation() {
• Ví dụ 5: printPersonalInformation();
printGrades();
• Các phương thức print và
}
println (gói java.io của Java) }
✓ print được nạp chồng 7 lần với 7 class Professor extends Person() {
kiểu dữ liệu đầu vào khác nhau: ...
Object, char, char[], int, foat v.v.. void printInformation() {
printPersonalInformation();
✓ Vì thế hoàn toàn có thể dùng
printResearchInterests();
print để in kí tự hoặc số nguyên, }
số thực được: }
System.out.print(‘c’);
System.out.print(3.14);
22
1 ❖Phân tích Overloading (tiếp)
• Một vài chú ý khi nạp chồng: class Test {
✓ Gán hợp lệ: a = b public static void main(String
args[]) {
Kiểu của b phải không nhỏ hơn kiểu của a.
double d;
Nếu kiểu của b lớn hơn kiểu của a: int i;
a = (kiểu của a) b. d = 5; // hợp lệ
Ví dụ 6: i = 3.5; // illega error
✓ Gọi phương thức hợp lệ: i = (int) 3.5; // hợp lệ
}
Ví dụ 7: gọi phương thức hợp lệ }
Ví dụ 8: gọi phương thức không hợp lệ
Vì thế, những phương thức phổ biến (kiểu như
System.out.println(..) nên nạp chồng
Ví dụ 9: Khi có nạp chồng
• Đây vẫn là hai thông điệp myPrint khác nhau
→Một hành vi mà ứng với mỗi đối tượng ở các lớp khác nhau nó được hiểu theo
các nghĩa khác nhau được cài đặt như thế nào? ❖/ 23
1 ❖ Khái niệm Overriding
• Đa hình được cài đặt bởi một khái niệm tương tự nạp
chồng nhưng hơi khác: method overriding với “override”
có nghĩa “vượt quyền”.
• Chỉ trong thừa kế mới có hiện tượng này.
• Method overriding: nếu một phương thức của lớp cơ sở
được định nghĩa lại tại lớp dẫn xuất thì định nghĩa tại lớp
cơ sở có thể bị “che” bởi định nghĩa tại lớp dẫn xuất.
• Toàn bộ thông điệp (cả tên và tham số) là hoàn toàn
giống nhau - điểm khác nhau là lớp đối tượng được nhận
thông điệp.

24
1 ❖ Khái niệm Overriding (tiếp)
• Ví dụ nhận được cùng một thông điệp jump() -nhảy:
– Kangaroo và Frog đều là lớp con của lớp Animal
– Một con kangaroo và một con ếch nhảy theo hai kiểu khác nhau.
– Chúng cùng có hành vi nhảy nhưng các hành vi này có nội dung
khác nhau.

❖/ 25
1 ❖Phân tích Overriding
• Ví dụ 10: Xét hành vi “draw”của các
lớp trong cây thừa kế hình bên:
– Thông điệp “draw” gửi cho một thể
hiện của mỗi lớp trên sẽ yêu cầu thể
hiện đó tự vẽ chính nó

– Có thể thực hiện được thông qua


method overriding: hành vi draw được
định nghĩa lại tại các lớp dẫn xuất.

26
1 ❖Phân tích Overriding (tiếp)
• Ví dụ 10: (tiếp) Point và Circle có hành vi draw() riêng
public class Point {
public Point(int x, int y,String color){...};
public void draw(){...}; // vẽ điểm
private int x,y;
}
public class Circle extends Point{
public Circle(int x, int y,String color, int
radius){...};
public void draw(){...}; // vẽ 1 đường tròn
private int radius;
}

Point p(0,0,”white”);
Circle c(100,100,”blue”,50);
p.draw(); // Vẽ 1 điểm trắng tại điểm (0,0)
c.draw(); // Vẽ một hình tròn màu xanh, bán kính 50 tâm tại điểm (100,100)
27
1 ❖Phân tích Overriding (tiếp)
• Ví dụ 11: Lớp Dog thừa kế từ lớp Animal
class Animal { public class Dog extends Animal {
public void print() { public void print() {
System.out.println("In tu lop cha System.out.println("In tu lop con Dog");
Animal"); }
} public String toString(){
public String toString(){ return "toString cua Animal";
return "toString cua }
Animal"; public static void main(String args[]) {
} Animal animal = new Animal();
} Dog dog = new Dog();
animal.print();
System.out.println(animal.toString());
dog.print();
System.out.println(dog.toString());
}
}
28
1 ❖Phân tích Overriding (tiếp)
• Ví dụ 11: (tiếp) animal.print();
System.out.println(animal.toString());
dog.print();
System.out.println(dog.toString());

• Phương thức print trong lớp Dog đã vượt quyền


phương thức print của lớp cha Animal.
• Phương thức toString của Animal và toString của
Dog đã vượt quyền phương thức toString của lớp
Object (vì ngầm định Animal thừa kế từ lớp Dog)

29
1 ❖Phân tích Overriding (tiếp)
• Đến đây bắt đầu nảy sinh nhiều rắc rối!
• Hãy nhớ lại ví dụ trong phần ẩn danh:
public class Homer {
char doh(char c) { } Câu hỏi để sinh viên tự trả lời:
float doh(float f) { } ✓Đây không còn là Method Overriding.
} ✓Vậy để phương thức ở lớp con có thể
public class Milhouse {} vượt quyền được phương thức của lớp
public class Bart extends Homer {
cha thì khi khai báo cần phải có điều
void doh(Milhouse m) {...}
} kiện gì?
public class Hide { ✓Gợi ý: Hãy nhìn vào tên phương thức,
public static void main(String args[]) { tham số truyền vào, kiểu trả về.
Bart b = new Bart();
b.doh(1);
b.doh('x');
b.doh(1.0f);
b.doh(new Milhouse());
}} 30
1 ❖Phân tích Overriding (tiếp)
• Vấn đề thứ hai: Quay trở lại vấn đề upcasting với cây Person-
Employee-Manager.
public class Person { Manager m = new Manager();
public void talk(){ Employee e = m;
System.out.println("Xin chao!"); Person p = e;
} p.talk(); // noi gi??
} e.talk(); // noi gi??
public class Employee extends Person{
public void talk(){
System.out.println(“Toi la mot nhan vien!");
} Là Person nhưng không nói
} Xin chào!
public class Manager extends Employee{ Là Employee nhưng lại nói:
public void talk(){ Toi la mot quan ly!
System.out.println(“Toi la mot quan ly!");
}
}
31
1 ❖Phân tích Overriding (tiếp)
• Có một bài toán rất hay trong đa hình. Đó là bài toán bò điên.
• Ví dụ 12: Có một con bò có tên và màu lông, có tiếng kêu phát ra là
"moo"
public class Cow{
public Cow(String name, String color){...};
public void makeASound()
{System.out.println("Moo");}
private String name,color;
}

Nếu tạo một đối tượng Cow, và gọi hành vi makeASound()


của nó, nó sẽ kêu “moo”:
Cow bob = new Cow(“Bob”, “brown”);
bob.makeASound(); // moo
Ta còn có thể tạo một đối tượng khác, được gán bởi đối tượng
bob, và làm cho nó kêu “moo”
Cow P_bob = bob;
p_bob.makeASound(); // moo 32
1 ❖Phân tích Overriding (tiếp)
• Ví dụ 12: (Tiếp) Bò điên (Mad cow): cũng là bò, nhưng chúng tưởng
mình là ếch và kêu "oap“.
public class MadCow extends Cow{
public MadCow(String name, String color){
super(name,color);
}
public void makeASound()
{System.out.println("Oap");}
}

Nhưng bò điên khá là rắc rối: nếu coi nó là bò điên, nó sẽ


hành động đúng là bò điên:
MadCow maddy = new madCow(“Maddy”, “white”);
maddy.makeASound(); // oap
Và nếu có:
MadCow p_maddy = maddy;
p_maddy.makeASound(); // vẫn là kêu oap
33
1 ❖Phân tích Overriding (tiếp)
• Ví dụ 12: Nhưng, có thể, trong hướng đối tượng, nếu thiết kế không tốt.
Bò điên vẫn kêu moo. Như đoạn lệnh sau trong lập trình C++:
MadCow maddy(“Maddy”, “white”);
maddy.makeASound(); // Kêu “oap”
MadCow * maddy = new MadCow(“Maddy”,
“white”);
maddy->makeASound(); // Kêu “oap”
MadCow* maddy = new madCow(“Maddy”,
“white”);
Cow* cow = maddy; // Coi bò điên là bò thường
Cow& rCow = maddy;// Coi bò điên là bò thường
rCow.makeASound(); // Thì nó lại kêu "moo"
cow->makeASound(); // Lại kêu “moo” ???

Còn trong Java: MadCow maddy = new MadCow("Maddy","white");


Cow p_bob = (Cow)maddy; // Coi bò điên là bò thường
p_bob.makeASound(); // Thì nó vẫn là bò điên: kêu Oap
Nếu viết: MadCow maddy2 = (MadCow)bob; // run-time error 34
1 ❖Phân tích Overriding (tiếp)

• Vậy làm thế nào để bò điên lúc nào cũng


điên (kêu “oap”)?

1/ 35
2. Đa hình và liên kết phương thức
❖Đa hình và liên kết phương thức
• Thay cho câu hỏi trên, là câu hỏi khác: Java làm thế nào để tìm
đúng phương thức cần chạy mỗi khi ta có một lời gọi phương
thức?
• Khái niệm mới: function call binding/method call binding (tạm
dịch là liên kết lời gọi hàm/ liên kết lời gọi phương thức.
• Function call binding: là quy trình xác định khối mã hàm cần chạy
khi một lời gọi hàm được thực hiện
✓ Với C: rất dễ dàng vì với mỗi
tên hàm chỉ có một định nghĩa
hàm.

36
❖ Đa hình và liên kết phương thức (tiếp)

✓ Đa hình hàm: buộc trình biên dịch phải kiểm tra cả danh sách tham
số cùng tên hàm khi xác định liên kết
• Method call binding: lớp và hướng đối tượng
✓ Nếu lớp độc lập, không tham gia vào cây thừa kế nào, method call
binding giống function call binding.
✓ Nhưng có cây thừa kế thì phức tạp hơn.
→Liên quan tới một loại liên kết: liên kết tĩnh static binding.
❖/ 37
❖ Khái niệm đa hình tĩnh-static polymorphism

• Static function call binding hoặc static binding– liên kết tĩnh: là
quy trình liên kết một lời gọi hàm với một định nghĩa hàm tại thời
điểm biên dịch.
Do đó kiểu liên kết này còn được gọi là “compile-time binding”– liên
kết khi biên dịch, hoặc “early binding”– liên kết sớm.
• Đa hình tĩnh (dynamic polymorphism): là loại đa hình được cài
đặt bởi liên kết tĩnh.
• Cần phải hiểu rõ liên kết tĩnh: khi nào/tại sao nó xảy ra, khi nào/tại
sao nó gây rắc rối
→ Là chìa khoá để tìm được cách làm cho bò điên lúc nào cũng điên.
→ Quan trọng hơn: làm thế nào để có đa hình?
38
❖ Khái niệm đa hình tĩnh-static polymorphism (tiếp)

• Với liên kết tĩnh, quyết định “định nghĩa hàm nào được
chạy” được đưa ra tại thời điểm biên dịch, rất lâu trước khi
chương trình chạy.
• Cách này:
✓ Ưu điểm về tốc độ.
✓ Thích hợp cho các lời gọi hàm thông thường: mỗi lời gọi hàm chỉ
xác định duy nhất một định nghĩa hàm, kể cả trường hợp hàm
chồng.
✓ Phù hợp với các lớp độc lập không thuộc cây thừa kế nào: Mỗi lời
gọi phương thức đều xác định duy nhất một phương thức. 39
❖ Khái niệm đa hình tĩnh-static polymorphism (tiếp)
• Nhược điểm: trường hợp bò điên
MadCow maddy= new
viết bằng C++. Vì: MadCow(“Maddy”, “white”);
maddy.makeASound(); // Kêu “oap”
✓ Trình biên dịch sinh lời gọi hàm: thấy kiểu MadCow * maddy = new
tĩnh của rCow là Cow&, nên gọi MadCow(“Maddy”, “white”);
maddy->makeASound(); // Kêu “oap”
Cow::makeASound(): kêu moo MadCow* maddy = new
✓ Liên kết tĩnh không quan tâm đến con trỏ madCow(“Maddy”, “white”);
Cow* cow = maddy; // Coi bò điên là
(hoặc tham chiếu) có thể trỏ tới một đối
bò thường
tượng của một lớp dẫn xuất. Cow& rCow = maddy;// Coi bò điên là
✓ Địa chỉ đoạn mã cần chạy cho một lời gọi bò thường
rCow.makeASound(); // Thì nó lại kêu
hàm cụ thể là không đổi trong suốt thời gian "moo"
chương trình chạy. cow->makeASound(); // Lại kêu “moo”

✓ Liên kết tĩnh là mặc định. Vì thế đa hình Cow::makeASound(){


tĩnh là mặc định. ....
}
✓ Đó là lí do vì sao bò điên đôi lúc lại không
40
điên. `
❖ Khái niệm đa hình tĩnh-static polymorphism (tiếp)

• Tại sao Java lại không xảy ra hiện tượng này?

MadCow maddy = new MadCow("Maddy","white");


Cow p_bob = (Cow)maddy; // Coi bò điên là bò thường
p_bob.makeASound(); // Thì nó vẫn là bò điên: kêu Oap

❖/ 41
❖ Khái niệm đa hình động-dynamic polymorphism
• Dynamic function call binding hoặc dynamic binding– liên kết
động: là quy trình liên kết một lời gọi phương thức với một định
nghĩa phương thức tại thời gian chạy.
Do đó còn gọi là “run-time binding” – liên kết trong thời gian chạy hoặc
“late binding” – liên kết muộn: Khi chạy, chương trình xác định phương
thức phù hợp với đối tượng mới tiến hành chạy phương thức đó.
•Đa hình động (dynamic polymorphism): loại đa hình được cài
đặt bởi liên kết động.
•Với ví dụ bò điên:
MadCow maddy = new MadCow("Maddy","white");
Cow p_bob = (Cow)maddy; // Coi bò điên là bò thường
p_bob.makeASound(); // Vẫn là kêu Oap
phương thức makeASound() của lớp MadCow đã vượt quyền phương
thức makeASound của Cow (hay là đã "che" mất).
→p_bob chỉ nhìn thấy phương thức MadCow.makeASound().

2/ 42
3. Phương thức riêng
• Để ngăn hiện tượng method overriding.
❖Khai báo và cài đặt public class A {
• Phương thức riêng: quyền private. private void foo(){...}
}
• Cú pháp: public class B extends A{
private <type> <method name> (<parameters>) A.foo();...// không hợp lệ
{...} private void foo(){..}
}
• Cài đặt phương thức bình thường.
• Sử dụng: chỉ các phương thức trong cùng
lớp với phương thức đó mới truy cập ✓ B không thừa kế foo từ A.
✓ private void foo(){}: hiện tượng
được. overloading.
• Lớp con không thể truy cập được tới ✓ Method overriding không xảy
ra.
phương thức này
❖/ 43
❖ Sử dụng phương thức riêng để ngăn method overriding
• Phân tích ví dụ sau: public class Base {
private void f() { System.out.println("base
• Ví dụ 13: f()");}
public void show() { f(); }
}
public class Derived extends Base {
private void f() {
System.out.println("derived f()");
}
public static void main (String args[]) {
Derived d = new Derived();
Base b = d;
b.show(); //d.show() cũng tương tự
}
}

44
❖ Sử dụng phương thức riêng để ngăn method overriding (tiếp)
• Qua phân tích các ví dụ trong đa hình, một thực tế là:
– Trong nhiều trường hợp, việc cài đặt các phương thức ở lớp cha, rồi lại
cài đặt các phương thức ở lớp con vượt quyền phương thức này làm cho
việc cài đặt phương thức ở lớp cha mất ý nghĩa.

– Ví dụ phương thức talk của Person không để sử dụng.

– Hoặc đôi khi chúng ta định nghĩa lớp đối tượng ở gốc đó là một đối
tượng chung chung nào đó, không bao giờ tạo thể hiện cho các đối
tượng đó.
• Làm gì trong tình huống này?

3/ 45
4. Lớp trừu tượng và phương thức trừu tượng

❖Lớp trừu tượng - abstract class


• Lớp cha trong cây thừa kế khi không thực sự được dùng để tạo ra thể
hiện thì nó để làm gì?
Câu trả lời là lớp cha được tạo ra nhằm mục đích tạo ra một giao diện
chung (có chung các hành vi gì?) cho tất cả các lớp được thừa kế từ
lớp cha đó.
• Khai báo một lớp là trừu tượng: abstract class Base{}
• Lớp trừu tượng: tạo thể hiện cho lớp đó, hay cài đặt một phương thức
nào đó cho lớp đó không có ý nghĩa gì.
Java ngăn điều đó xảy ra ngay khi biên dịch để chặn người dùng .

46
4 ❖ Lớp trừu tượng - abstract class (tiếp)
Ví dụ 14: Các đoạn lệnh sẽ gây lỗi/không lỗi biên dịch
abstract class Shape { class Circle extends Shape {
protected int x, y; int r;
Shape(int _x, int _y) { public Circle(int _x, int _y, int _r) {
x = _x; super(_x, _y);
y = _y; r = _r;
} }
} }
Shape s = new Shape(10, 10) Circle c = new Circle(10,10,5); //hợp lệ.

✓ Chỉ có thể thừa kế lại Shape(int _x,int _y) ở lớp con của nó
✓ Các phương thức được cài đặt ở lớp trừu tượng chỉ phát huy tác dụng khi
các thể hiện của lớp con gọi tới các phương thức này.
✓ Tính chất: mọi thể hiện của lớp con cũng được coi như thể hiện của lớp
cha càng được biểu hiện rõ.
❖/ 47
4 ❖ Phương thức trừu tượng - abstract method
• Để thống nhất giao diện: có thể khai báo các phương thức tại lớp cha,
lớp con cài đặt lại. Các lớp con khác nhau có cách cài đặt khác nhau.
• Đưa ra khái niệm: Phương thức trừu tượng- abstract method.
Một phương thức ở lớp cha được khai báo là phương thức trừu tượng
thì bắt buộc phải định nghĩa lại tại lớp dẫn xuất.
•Ví dụ 15: Cây thừa kế Shape – Circle
Có thể cài thêm một lớp Rectangle (hình chữ nhật) cũng thừa kế từ
Shape, cài đặt lại phương thức draw và erase theo cách của riêng nó.
•Tóm lại: Phương thức của lớp cha nào mà lớp con cần và có các cách
thể hiện khác nhau thì cài đặt là phương thức ảo.
•Câu hỏi cho sinh viên tự trả lời: lớp không trừu tượng có thể khai
báo phương thức trừu tượng hay không?
4/ 48
5. Đa thừa kế và Giao diện
❖Khái niệm giao diện - Interface trong Java
• Java không cho phép đa thừa kế ở điểm: một lớp con có nhiều lớp cha.
Thay vào đó, Java phát triển khái niệm khác: giao diện - interface.
– Interface: mức trừu tượng cao hơn lớp trừu tượng.
– Có thể coi nó là lớp trừu tượng hoàn toàn.
– Cho phép trình biên dịch tạo ra 1 dạng giống như lớp trừu tượng: cũng có
khai báo phương thức nhưng không cần cài đặt.
– Một interface có thể chứa dữ liệu nhưng nó phải là static hoặc final: ????
– Interface không cho phép tạo thể hiện.
• Tóm lại, một interface bao gồm:
– Phương thức trừu tượng: abstract method
– Hằng số: static hoặc final
– Mặc định là public
❖/ 49
❖Khái niệm giao diện - Interface trong Java (tiếp)
• Khai báo giao diện: interface Action {
void moveTo(int x, int y);
interface <tên giao diện>{ void erase();
[foo definition;] void draw();}
class Shape implements Action {
[static data;]
protected int x, y;
[final data;] public Shape() {...}
} public Shape(int _x, int _y) {...}
public void moveTo(int x1, int y1) {
• Cài đặt giao diện: dùng từ erase();
khóa implements x = x1;
class <tên lớp > implements y = y1;
draw();
<tên giao diện>{ }
foo declaration; }
}
50
❖ Lớp trừu tượng cài đặt giao diện
• Lớp trừu tượng cũng có thể cài interface Action {
đặt được giao diện. void moveTo(int x, int y);
void erase();
• Khi đó, các phương thức của void draw();}
giao diện sẽ không bị yêu cầu abstract class Shape implements Action
phải cài đặt trong lớp trừu {
tượng. Có nghĩa là đoạn protected int x, y;
chương trình sau hoàn toàn public Shape() {...}
public Shape(int _x, int _y) {...}
hợp lệ: public void moveTo(int x1, int y1) {
• Ví dụ 17: erase();
x = x1;
• Tuy nhiên lớp Circle thừa kế
y = y1;
từ Shape vẫn bị đòi hỏi phải draw();
cài đặt lại erase() và draw(): }
}

51
❖ Lớp trừu tượng cài đặt giao diện (tiếp)
interface Action { class Circle extends Shape {
void moveTo(int x, int y); int r;
void erase(); public Circle(int _x, int _y, int _r) {
void draw();} super(_x, _y);
abstract class Shape implements Action r = _r;
{ draw();
protected int x, y; }
public Shape() {...} public void erase() {.....}
public Shape(int _x, int _y) {...} public void draw() {......}
public void moveTo(int x1, int y1) { }
erase();
x = x1;
✓ Cài đặt giống ví dụ 15
y = y1;
✓ Kết quả in ra cũng giống.
draw();}
✓ Shape cài đặt giao diện Action
}
cũng giống như Shape khai báo các
phương thức draw, erase, moveTo
trong thân của nó vậy.
52
❖ Lớp trừu tượng cài đặt giao diện (tiếp)
• Có thể khai báo lớp Rectangle không kế thừa từa Shape mà vẫn có các hành
vi draw, erase, moveTo bằng cách cài đặt trực tiếp giao diện Action.
class Rectangle implements Action{
interface Action { int width, height, x,y;
void moveTo(int x, int y); public Rectangle(int _x, int _y, int _width, int
void erase(); _height) {
void draw(); x= _x;
} y=_y;
✓ Cài đặt giống ví dụ 15 width = _width;
height = _height;
✓ Kết quả in ra cũng giống.
draw();
✓ Chính tính linh động đó của }
Interface đã tạo ra khả năng public void erase() {...}
tham gia vào đa thừa kế: thay thế public void draw() {...}
cho một lớp con thừa kế từ nhiều public void moveTo(int x1, int y1) {...}
}
lớp cha.
❖/ 53
❖ Cài đặt nhiều giao diện thay vì đa thừa kế
• Java không cho phép đa kế thừa từ nhiều lớp cơ sở: để đảm bảo
tính dễ hiểu và hạn chế xung đột.
• Nhưng Java cho phép có thể cài đặt đồng thời nhiều giao diện.
• Hơn nữa, giao diện không có các cài đặt cho các phương thức, nên
nó rất tiết kiệm bộ nhớ.
• Để cài đặt nhiều giao diện chỉ việc liệt kê tên các giao diện cách
nhau bởi dấu phẩy ,
• Cú pháp: Và cài đặt interface:
Khai báo interface:
interface A{...} class D implements A,B,C {
interface B{...} ....
interface C{...} }

• Và nhớ rằng cài đặt bao nhiêu giao diện thì phải cài đặt bấy
nhiêu phương thức của giao diện đó.
54
❖ Cài đặt nhiều giao diện thay vì đa thừa kế (tiếp)
• Một lớp vừa có thể cài đặt nhiều giao diện, vừa có thể thừa kế được từ
một lớp khác. Ví dụ 18: lớp Hero mô tả các siêu nhân có thể đánh
nhau, bơi lội hoặc là bay.
interface CanFight { class Hero extends ActionCharacter
void fight(); implements CanFight, CanSwim, CanFly {
} public void swim() {}
interface CanSwim { public void fly() {} } // end Hero
void swim(); public class Adventure {
} static void t(CanFight x) { x.fight(); }
interface CanFly { static void u(CanSwim x) { x.swim(); }
void fly(); static void v(CanFly x) { x.fly(); }
} static void w(ActionCharacter x) { x.fight(); }
public static void main(String args[]) {
class ActionCharacter Hero spiderman = new Hero();
{ t(spiderman); // Hoạt động giống một CanFight
public void fight() u(spiderman); // Giống một CanSwim
{} v(spiderman); // Giống một CanFly
} w(spiderman); // Giống một ActionCharacter}} 55
❖ Cài đặt nhiều giao diện thay vì đa thừa kế (tiếp)

• Thấy rằng giữa interface và lớp cài đặt nó cũng có khả năng
upcasting.

• Như ví dụ trên khai báo biến spiderman là một thể hiện của
lớp Hero nhưng vẫn dùng nó làm tham số đầu vào cho các
phương thức t,u,v,w.

• Tóm lại: Giao diện giống như lớp, nhưng nó không tạo thể
hiện và đòi hỏi phải cài đặt lại các phương thức tại các lớp
cài đặt nó.

❖/ 56
❖ Một số xung đột ( Học viên tự tìm hiểu)
• Hãy cẩn thận các phương thức có thể chồng chéo lên nhau gây ra
hiện tượng nạp chồng overloading
• Ví dụ 19: Trong cùng 1 file, viết đoạn chương trình sau:
interface I1 { void f(); } ✓ Lớp C2: phương thức int f(int i) của I2,
interface I2 { int f(int i); } phương thức void f() của I1 được cài đặt.
interface I3 { int f(); }
class C { ✓ Hai phương thức này đã nạp chồng lẫn
public int f() { return 2; } nhau. Trong lớp C3, cài đặt phương thức
} int f(int i) của I2, nhưng nó lại vượt
class C2 implements I1, I2 {
public void f() {} quyền overriding phương thức int f(int i)
public int f(int i) { return 1; } của C.
} ✓ Nếu thay int f(int i) trong lớp C bằng int
class C3 extends C implements I2 {
public int f(int i) { return 1; }} f(), thì có chuyện gì xảy ra?

57
❖ Một số xung đột (tiếp)

• Ví dụ 19: (tiếp) Khai báo thêm C4


interface I1 { void f(); }
interface I2 { int f(int i); } ✓ Ở lớp C3: hiện tượng nạp chồng xảy
interface I3 { int f(); }
class C { ra
public int f() { return 1; }
✓ Ở lớp C4: public int f() vượt quyền
}
class C3 extends C implements I2 { overriding phương thức int f() của C.
public int f(int i) { return 1; }}
class C4 extends C implements I3 { →Tạo ra xung đột thứ nhất.
public int f() { return 1; }
} ✓ Xung đột này không gây ra lỗi cho
chương trình, nhưng nó làm chương
trình mất tính rõ ràng.
58
❖ Một số xung đột (tiếp)
• Tiếp, nhìn vào 2 câu lệnh:
✓ C5 thừa kế từ C, mà C khai báo và cài đặt
interface I1 { void f(); } phương thức int f(), nhưng phương thức này
interface I2 { int f(int i); } không thể nạp chồng được với phương thức
interface I3 { int f(); } void f() trong I1.
class C { ✓ Đối với C5 void f() chưa được cài đặt, dù đã
public int f() { return 1; } cài đặt một phương thức void f() trong C2.
} ✓ Điều đó đúng vì lúc đó void f() được xem như
class C2 implements I1, I2 { là hành vi của C2 mà thôi.
public void f() {}
✓ Câu lệnh này lại không báo lỗi, dù rằng C5
public int f(int i) { return 1; }
} chưa cài đặt phương thức int f() nhưng có lớp
class C đã cài đặt int f() rồi nên được chấp nhận.
class C5
C5 extends
extends C C implements
implements I1 I3{}//
{}
báo lỗi ✓ Hạn chế cho các lớp overloading, overriding
interface I4 extends I1, I3 {} // báo lỗi chồng chéo nhau.
✓ Chính vì thế đa thừa kế mới phức tạp và vẫn
nên cẩn thận khi dùng.
59
❖ Một số xung đột (tiếp)
• Tiếp tục: interface I1 { void f(); }
interface I2 { int f(int i); }
interface I3 { int f(); }
interface I4 extends I1, I3 {} // báo lỗi

✓ interface I4 extends I1, I3 {} : I3 and I1 are


incompatible, both define f(), but with
✓Tóm lại: Java cho phép một
unrelated return types.
✓ Có nghĩa là I1 và I3 không tương thích, cả hai giao diện thừa kế từ nhiều giao
cùng định nghĩa f() nhưng với các kiểu trả về diện khác.
không liên quan gì dến nhau. Nhưng không cho phép các
✓ Nếu bỏ đi 1 trong 2 Interface I1 hoặc I3, câu giao diện đó có các phương
lệnh lại hợp lệ. thức trùng tên và khác kiểu trả
✓ Nếu thay I1 bằng I2, câu lệnh cũng lại hợp lệ. về.
✓ Nếu đổi tên phương thức f trong I1 thành tên
khác ví dụ fx, câu lệnh cũng lại hợp lệ. ❖/ 60
❖ Mở rộng lớp trừu tượng và giao diện
• Java cung cấp các khả năng sau:
✓ Một giao diện có thể thừa kế từ một hay nhiều giao diện khác.
✓ Một lớp trừu tượng có thể thừa kế một lớp trừu tượng khác, đồng thời
cũng có thể cài đặt nhiều giao diện khác.
• Như vậy các câu lệnh sau hoàn toàn hợp lệ:
interface I1 {}
interface I2 {}
interface I3 extends I1, I2 {}
abstract class A1 {}
abstract class A2 extends A1 implements I1, I2 {}
• Đó là cách để mở rộng lớp trừu tượng hoặc mở rộng giao diện.
• Như vậy lớp trừu tượng có thể có phương thức và thuộc tính. Còn
giao diện thì có thể đa kế thừa. Chính vì thế người ta nói giao diện
là một lớp trừu tượng thuần túy nhưng ở mức cao hơn.
❖/ 61
❖ Thêm một số ứng dụng khác của giao diện
• Dùng giao diện để tạo ra một nhóm các hằng. Lúc này interface
giống như kiểu liệt kê mà ta đã gặp trong Pascal hay C:
• Ví dụ 20: Liệt kê các tháng trong năm
public interface Months {
int
JANUARY = 1, FEBRUARY = 2, MARCH = 3,
APRIL = 4, MAY = 5, JUNE = 6, JULY = 7,
AUGUST = 8, SEPTEMBER = 9, OCTOBER = 10,
NOVEMBER = 11, DECEMBER = 12;
}
• Một cách tự động, các hằng số trên đều có quyền public nên các
lớp sau đó cài đặt interface này đều có thể truy nhập vào các hằng
số trên.
• Các số trên là các hằng dữ liệu tĩnh được khởi tạo trong thời gian
biên dịch. ❖/ 62
❖ Thêm một số ứng dụng khác của giao diện (tiếp)
• Có thể dùng interface để khởi tạo các
trường dữ liệu
• Ví dụ 21: Khởi tạo các biến bằng các giá
trị ngẫu nhiên
public interface RandVals { public class TestRandVals {
public static void main(String args[])
int rint = (int)(Math.random() * 10);
{
long rlong = (long)(Math.random() * System.out.println(RandVals.rint);
10); System.out.println(RandVals.rlong);
float rfloat = (float)(Math.random() * System.out.println(RandVals.rfloat);
10); System.out.println(RandVals.rdouble)
double rdouble = Math.random() * 10; ;
} }}
• Các trường dữ liệu trên sẽ có kiểu final
static, giá trị của các dữ liệu được khởi
tạo trong thời gian chạy.
• Có thể sử dụng interface trên như bên:
5/ 63
Tổng kết
• Như vậy chúng ta tạm thời kết thúc các kiến thức cở sở về đa
hình trong lập trình hướng đối tượng và được cụ thể hóa bằng
ngôn ngữ lập trình Java.
• Cái mà chúng ta cần nhớ trong đa hình đó là:
– overloading để các phương thức có quyền trùng tên nhưng có thể
khác signature;
– overriding để một phương thức ở lớp con có thể vượt quyền một
phương thức giống hệt như vậy được cài đặt ở lớp cha.
• Thêm vào đó interface - giao diện là cách để đa thừa kế trong
Java với các lớp cài đặt giao diện thì phải định nghĩa các
phương thức đã được khai báo trong giao diện đó.
– Tuy nhiên cẩn thận khi đa thừa kế: các phương thức bị chồng chéo
64
Home

Q&A

65
Phần ví dụ trong bài

66
1 ❖Phân tích Overloading (tiếp)
Ví dụ 7: gọi hợp lệ Ví dụ 8: gọi không hợp lệ
class Test { class Test {
public static void main(String public static void main(String
args[]) { args[]) {
myPrint(5); // hợp lệ myPrint(5.0); // không hợp lệ
} }
static void myPrint(double static void myPrint(int i) {
d) { System.out.println(i);
System.out.println(d); }
} }
}

Kết quả in ra là: 5.0.


Không hợp lệ: giống như với phép gán double
Hợp lệ: giống như với phép gán double d = 5
int i = 5.0
67
1 ❖Phân tích Overloading (tiếp)
Ví dụ 9: Nạp chồng 2 phương thức myPrint()

class Test {
public static void main(String args[]) {
myPrint(5);
myPrint(5.0);
}
static void myPrint(double d) {
System.out.println("double: " + d);
}
static void myPrint(int i) {
System.out.println("int: " + i);
}
}

68
4 ❖ Phương thức trừu tượng - abstract method
Ví dụ 15: Shape khai báo các phương thức ảo, Circle cài đặt chúng
abstract class Shape { class Circle extends Shape {
protected int x, y; int r;
public void moveTo(int x1, int y1) { public Circle(int _x, int _y, int _r) {
erase(); super(_x, _y);
x = x1; r = _r;
y = y1; draw();
draw(); }
} public void erase() {
Shape(int _x, int _y) { System.out.println("Xoa o diem (" + x +
x = _x; "," + y + ")");}
y = _y; public void draw() {
} System.out.println("Tao o diem (" + x +
abstract public void erase(); "," + y + ")" +
abstract public void draw(); } " duong tron ban kinh"+r);}
}

69
4 ❖ Phương thức trừu tượng - abstract method (tiếp)
Ví dụ 15: Kết quả của khai báo: Circle c = new Circle (10,10,5);

70
4 ❖ Phương thức trừu tượng - abstract method (tiếp)
Ví dụ 15: Cài đặt thêm Rectangle
abstract class Shape { class Rectangle extends Shape {
protected int x, y; int width, height;
public void moveTo(int x1, int y1) { public Rectangle(int _x, int _y, int _width,
erase(); int _height) {
x = x1; super(_x, _y);
y = y1; width = _width;
draw(); height = _height;
} draw();}
Shape(int _x, int _y) { public void erase() {
x = _x; System.out.println("Xoa o diem ("+ x +
y = _y; "," + y + ")");}
} public void draw() {
abstract public void erase(); System.out.println("Tao o diem (" + x +
abstract public void draw(); } "," + y + ")" + "hinh chu nhat chieu dai“
+width+"chieu rong" +height);
}
} 71
4 ❖ Phương thức trừu tượng - abstract method (tiếp)
Ví dụ 15: Kết quả lần này khi thêm dòng khai báo: Rectangle r = new
Rectangle (20,10,30,5)

72
5 ❖ Khai báo và cài đặt giao diện
Ví dụ 16: Cài đặt giao diện Action cho Circle
interface Action {
void moveTo(int x, int y); Trong một file .java, có thể định
void erase(); nghĩa nhiều giao diện hoặc có thể
void draw(); vừa định nghĩa giao diện vừa định
class Circle1 implements Action { nghĩa các lớp sử dụng nó.
int x, y, r;
Circle1(int _x, int _y, int _r) { ... }
public void erase() {...}
public void draw() {...}
public void moveTo(int x1, int y1) {...}
}

73

You might also like