Covariance và Contravariance trong PHP


1. Mở đầu – Tại sao mình nên biết điều này?
Bạn mới bắt đầu học lập trình với PHP, và đột nhiên nghe đến “covariance”, “contravariance” – nghe khá… hơi phức tạp nhỉ? Nhưng thực ra, đây là những khái niệm quan trọng liên quan đến cách lớp con (child class) “làm khác” một chút so với lớp cha (parent class), mà vẫn đảm bảo code của bạn chạy ổn định. Hiểu được chúng giúp bạn viết code chuẩn hơn, an toàn hơn và tránh lỗi kiểu “thầm lặng” khó phát hiện.
2. Nội dung chính: Hiểu từ cơ bản đến sâu
2.1. Covariance – Làm cho loại trả về (return type) “cụ thể hơn”
Khái niệm đơn giản: Covariance là khi lớp con cho phép phương thức trả về một loại dữ liệu cụ thể hơn so với lớp cha.
Ví dụ thực tế 1: Bạn có một phương thức
getFileSize()
trong lớp cha, trả về kiểufloat|int
(tức có thể là số thực hoặc số nguyên). Lớp con lại override phương thức này nhưng chỉ trả vềint
– thế là hợp lệ rồi đó. PHP cho phép như vậy, vìint
là “cụ thể hơn” (DEV Community).Ví dụ thực tế 2: Trong đời sống, bạn cung cấp “dạng bánh” tổng quát (cake). Lớp con lại đưa ra “bánh socola” – cụ thể hơn, nhưng vẫn hợp lý.
declare(strict_types=1);
class BaseReport {
public function getSize(): float|int {
// Trả về float hoặc int
return 42; // số nguyên vẫn được chấp nhận
}
}
class DetailedReport extends BaseReport {
public function getSize(): int {
// Cụ thể hơn, chỉ trả về số nguyên
return 42;
}
}
Cảnh báo nhẹ: Đừng cho rằng cái gì cũng “nhiều hơn là tốt hơn”. Covariance chỉ áp dụng cho kiểu trả về trong phương thức – PHP hỗ trợ đúng như vậy (php.net, DEV Community).
2.2. Contravariance – Ngược lại: tham số nhập vào “rộng hơn”
Khái niệm đơn giản: Contravariance là khi lớp con cho phép phương thức chấp nhận tham số không cụ thể hơn, tức rộng hơn (loại cha → loại tổng quát hơn).
Trước PHP 7.2, chỉ hỗ trợ một phần; từ PHP 7.4 trở đi, PHP đã hỗ trợ đầy đủ chính sách variance: covariance với return types, và contravariance với parameter types (php.net, WordPress at Your Fingertips).
Ví dụ thực tế 1: Nếu lớp cha phương thức:
function process(Animal $a)
(chỉ nhận Animal), thì lớp con có thể viết làfunction process(Mammal $m)
? Không ổn – đó là covariance tham số, PHP không cho. Nhưng nếu lớp cha làfunction process(Mammal $m)
, và lớp con chấp nhậnfunction process(Animal $a)
– rộng hơn thì được, nghĩa là contravariance tham số.Ví dụ thực tế 2: Giống như bạn mời “chơi game” chỉ dành cho “học sinh lớp 10 trở lên” (parent). Trong phạm vi “chỉ học sinh cấp 2 trở lên” (class con) vẫn chấp nhận – ok. Nhưng nếu lớp con lại giới hạn “chỉ học sinh lớp 12” thì quá hẹp – sẽ loại mất người thuộc lớp 10–11 (không hợp lệ).
class ParentClass {
public function handle(object $obj) {
// ...
}
}
class ChildClass extends ParentClass {
public function handle(string $str) {
// PHP không cho phép – tham số “hẹp hơn”
}
}
Ở trên là ví dụ sai, vì lớp con hẹp hơn tham số của lớp cha. PHP sẽ báo lỗi (DEV Community, php.net).
2.3. Tổng hợp dễ nhớ
Tác vụ | Cho phép không? | Giải thích ngắn | |
Covariance return | Có | Trả về kiểu cụ thể hơn (eg: int thay cho float | int) |
Covariance tham số | Không | Không được làm hẹp tham số | |
Contravariance tham số | Có (PHP ≥7.4) | Cho phép tham số rộng hơn so với lớp cha | |
Contravariance return | Không | Không được trả về loại tổng quát hơn |
3. Thử thách nhỏ
- Hãy nghĩ đến một tình huống trong đời sống hoặc lập trình mà bạn muốn “lớp con làm lỏng điều kiện hơn” cho tham số so với lớp cha – đó chính là contravariance.
- Ngược lại, khi nào bạn muốn trả về kết quả cụ thể hơn – đó là covariance. Bạn thử nghĩ xem?
4. Kết luận – Cứ “bottom-up” là ổn!
- Covariance giúp lớp con trả về loại dữ liệu cụ thể hơn, PHP hỗ trợ với return type từ PHP 7.4 trở lên.
- Contravariance cho phép lớp con nhận tham số rộng hơn, PHP hỗ trợ từ PHP 7.4.
- Việc hiểu rõ giúp bạn override phương thức an toàn mà không gây lỗi kiểu không mong muốn.
Lời khuyên thực tế: Luôn kiểm tra xem phiên bản PHP project bạn đang dùng là gì (ít nhất phải ≥7.4 để áp dụng đầy đủ). Khi override, suy nghĩ như người trong nghề: “Nếu mình trả về cụ thể hơn thì ok (covariance), nhưng nhập vào thì phải rộng hơn hoặc bằng, không được hẹp quá (contravariance).”
5. Tài liệu tham khảo đáng tin cậy
- Bài gốc Ash Allen trên DEV & trang cá nhân: Covariance and Contravariance in PHP (DEV Community, Ashallen Design)
- Tài liệu chính thức PHP Manual về variance – có chi tiết về covariance/contravariance hỗ trợ từ PHP 7.2/7.4 (php.net)
- Bài giải thích formal hơn về type variance (ngôn ngữ khác) nếu bạn tò mò thêm (Wikipedia)
Chúc bạn học vui và viết code càng ngày càng “chuẩn và mượt” nhé! Nếu cần mình giải thích thêm phần nào, cứ hỏi nhé
Subscribe to my newsletter
Read articles from Binlerdev directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
