Menu
CoddyTech

Đa hình (polymorphism) là gì?

Trong lập trình, đa hình (polymorphism) là khả năng một đoạn code, như một lời gọi hàm hay một tên phương thức, làm việc được với giá trị thuộc nhiều kiểu khác nhau, trong đó mỗi kiểu tự cung cấp hành vi riêng. Gọi shape.area() sẽ chạy cách tính khác nhau cho hình tròn và hình vuông.

Tác giả Kevin Spektor, Co-founder & CTO

Cập nhật 24 tháng 9, 2026

Trong Python, len("hello") trả về 5, len([4, 8, 15]) trả về 3, và len({"a": 1}) trả về 1. Đó là một tên hàm dùng cho ba kiểu, và mỗi kiểu đếm theo cách riêng: chuỗi đếm ký tự, list đếm phần tử, từ điển đếm khóa. Từ này có gốc Hy Lạp: poly nghĩa là nhiều, morph nghĩa là hình dạng. Một lời gọi, nhiều dạng hành vi.

Đa hình hoạt động thế nào

Code gọi một phương thức theo tên, như shape.area(). Khi dòng đó chạy, ngôn ngữ xem đối tượng thực tế là gì và tìm area trên chính lớp của đối tượng đó. Việc tra cứu này gọi là dynamic dispatch (điều phối động), và nhờ nó một dòng code có thể làm những việc khác nhau với những đối tượng khác nhau:

Circle 3.14
Square 9
Circle 12.57

Python tìm phương thức bằng cách tra lớp của đối tượng trước, rồi đến các lớp cha theo một thứ tự cố định gọi là thứ tự phân giải phương thức (method resolution order). Nếu Square không có area() riêng, việc tìm kiếm sẽ đến Shape.area() và gây ra NotImplementedError, một tín hiệu cho biết mọi lớp con đều phải tự cung cấp phương thức này.

Vòng lặp không bao giờ kiểm tra nó đang giữ loại hình nào. Để thêm tam giác, bạn viết lớp Triangle có area() riêng, và vòng lặp vẫn chạy mà không cần sửa. Nếu không có đa hình, vòng lặp sẽ cần một nhánh if isinstance(...) cho mỗi hình, và phải sửa lại mỗi khi có hình mới.

Các loại đa hình

LoạiTên gọi khácTrông như thế nàoKhi nào phiên bản được chọn
Đa hình kiểu conĐa hình lúc chạy, ghi đè phương thứcLớp con thay thế một phương thức của lớp chaTrong lúc chương trình chạy
Đa hình ad hocNạp chồngMột tên, nhiều phiên bản cho các kiểu tham số khác nhau; cả nạp chồng toán tửLúc biên dịch trong Java và C++
Đa hình tham sốGenerics, templateMột đoạn code dùng được cho mọi kiểu: List<T> trong Java, vector<T> trong C++Lúc biên dịch

Khi nói "đa hình" trong lập trình hướng đối tượng mà không nói rõ hơn, người ta gần như luôn muốn nói đến hàng đầu tiên: đa hình kiểu con thông qua ghi đè.

Python có cả ba loại theo cách riêng của nó. Ghi đè hoạt động như ví dụ các hình ở trên. Các toán tử như + là đa hình ad hoc. Những hàm như max() và sorted() mang tinh thần đa hình tham số: chúng nhận list số, chuỗi hay ngày tháng, miễn là các phần tử so sánh được với nhau, và type hint như list[int] hoặc TypeVar có thể ghi rõ điều đó.

Đa hình lúc biên dịch và lúc chạy trong Java

Java có cả hai loại, và câu hỏi phỏng vấn thường hỏi về sự khác biệt này:

class Animal { String sound() { return "..."; } }
class Dog extends Animal {
    @Override String sound() { return "Woof"; }
}

Animal pet = new Dog();
pet.sound();          // "Woof": chosen at run time from the object

int add(int a, int b)          { return a + b; }
double add(double a, double b) { return a + b; }
add(2, 3);            // int version, chosen by the compiler
add(2.5, 1.0);        // double version

Biến pet có kiểu Animal, vậy mà phiên bản của Dog lại chạy, vì lựa chọn phụ thuộc vào đối tượng chứ không phải vào biến. Hai phương thức add là nạp chồng: trình biên dịch chọn một trong hai dựa vào kiểu của đối số trước khi chương trình chạy. Interface mang lại hành vi lúc chạy tương tự mà không cần dùng chung code: bất kỳ lớp nào cài đặt Comparable đều sắp xếp được bằng Collections.sort. C++ hoạt động giống vậy với một khác biệt: một phương thức chỉ được điều phối lúc chạy nếu được khai báo virtual. Xem đa hình trong Java, nạp chồng phương thức và hàm ảo trong C++.

Đa hình trong Python: duck typing và toán tử

Python không yêu cầu một lớp cha chung. Nếu đối tượng có phương thức mà code gọi tới, mọi thứ sẽ chạy: "nếu nó đi như vịt và kêu như vịt thì nó là vịt". Toán tử cũng có tính đa hình. Viết a + b sẽ gọi a.__add__(b), nên các lớp của bạn có thể hỗ trợ +, len() và print():

3 abcd [1, 2]
Vector(11, 22)
5 3 2

Kế thừa là một cách để có đa hình, không phải điều bắt buộc. Vector không kế thừa từ lớp đặc biệt nào, vậy mà len() và + vẫn đối xử với nó như một kiểu có sẵn. Tài liệu kế thừa trong Python trình bày cách dùng lớp con.

Những lỗi thường gặp

Mong Python nạp chồng theo chữ ký hàm. Một def thứ hai cùng tên sẽ âm thầm thay thế cái đầu tiên:

Hi Ana Silva
Traceback (most recent call last):
  ...
TypeError: greet() missing 1 required positional argument: 'last'

Hãy dùng đối số mặc định thay vào đó: def greet(first, last="").

Quên virtual trong C++. Thiếu nó, việc gọi phương thức qua con trỏ đến lớp cơ sở sẽ chạy phiên bản của lớp cơ sở, kể cả khi đối tượng là lớp con.

Ghi đè làm thay đổi ý nghĩa. Nếu area() của một lớp con lại trả về chu vi, mọi nơi gọi tin vào area() đều sai. Phương thức ghi đè phải giữ đúng cam kết của lớp cha; quy tắc này được gọi là nguyên lý thay thế Liskov.

Kiểm tra kiểu ở khắp nơi. Một chuỗi nhánh if type(x) == ... thường là dấu hiệu cho thấy mỗi kiểu nên tự mang phương thức của riêng nó.

Đọc tiếp

Đa hình đi cùng với tính đóng gói, thứ bảo vệ dữ liệu của mỗi đối tượng, và trừu tượng hóa, thứ định nghĩa giao diện chung mà các kiểu cài đặt. Luyện ghi đè trong tài liệu về lớp trong Python, hoặc thử khóa học Java, nơi nạp chồng và ghi đè xuất hiện cạnh nhau.

Câu hỏi thường gặp

Ví dụ về đa hình là gì?
Toán tử + trong Python: 1 + 2 cộng hai số, "ab" + "cd" nối hai chuỗi và [1] + [2] nối hai list. Cùng một ký hiệu dùng được cho ba kiểu, và mỗi kiểu tự quyết định nó nghĩa là gì. Trong code hướng đối tượng, ví dụ kinh điển là một danh sách các hình, trong đó mỗi hình trả lời area() bằng công thức riêng.
Đa hình và kế thừa khác nhau thế nào?

Kế thừa cho phép một lớp dùng lại code của lớp cha. Đa hình cho phép code xử lý nhiều kiểu khác nhau qua một giao diện chung. Kế thừa là một cách phổ biến để có đa hình, vì lớp con có thể ghi đè phương thức của lớp cha, nhưng duck typing của Python và interface của Java mang lại đa hình mà không cần dùng chung đoạn code nào.

Đa hình trong sinh học là gì?

Trong sinh học, đa hình nghĩa là hai hay nhiều dạng của một tính trạng hoặc một gen cùng tồn tại trong một quần thể, như các nhóm máu A, B, AB và O ở người. Thuật ngữ này có chung gốc Hy Lạp, "nhiều hình dạng", với thuật ngữ trong lập trình, nhưng hai khái niệm không liên quan đến nhau.

Python có hỗ trợ nạp chồng phương thức không?
Không theo kiểu hay số lượng tham số như Java và C++. Một def thứ hai cùng tên sẽ thay thế cái đầu tiên. Code Python đạt được hiệu quả tương tự bằng đối số mặc định, *args, hoặc functools.singledispatch, công cụ chọn hàm dựa trên kiểu của đối số đầu tiên.
Vì sao đa hình hữu ích?
Nó cho phép bạn thêm kiểu mới mà không phải sửa code đang dùng chúng. Một chương trình vẽ gọi shape.draw() trên mọi hình không cần sửa gì khi bạn thêm lớp tam giác; bạn chỉ cần viết lớp mới. Nếu không có đa hình, vòng lặp đó sẽ phải thêm một nhánh if cho mỗi kiểu.
Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu