Giải pháp chuyển đổi số trọn gói cho doanh nghiệp Việt
Với bài học hôm nay, mình sẽ bổ sung kiến thức tiếp theo trong phần kiến thức về kế thừa. Nếu như ở bài 21, các bạn đã biết cách thức sử dụng từ khóa extends để thể hiện sự kế thừa từ một lớp tới một lớp khác. Và khi đó, bạn cũng làm quen được việc tận dụng lại tất cả các giá trị từ một lớp cha (hay lớp cơ sở) để lại cho lớp con (hay lớp dẫn xuất), lúc đó mình gọi đây là sự dùng lại trong kế thừa. Hôm nay chúng ta sẽ xem đến một khía cạnh tiếp theo trong kế thừa, nó không còn ý nghĩa dùng lại nữa, mà là sự phủ quyết (overriding).
Mình quen dùng từ phủ quyết, có thể là do thói quen từ thời còn sử dụng C++ hay C#. Sang đến ngôn ngữ Java mình thấy người ta hay gọi là ghi đè. Bạn cũng có thể không cần quan tâm đến hai từ tiếng Việt này, mà chỉ cần nhớ từ tiếng Anh của nó là overriding là được.
Như mình có nói ở trên, khi bạn làm quen với kế thừa, bạn biết rằng khái niệm này ám chỉ đặc tính dùng lại mà chúng ta đã tìm hiểu sơ qua ở bài 21, đặc tính này cho phép các lớp con có thể mặc định có được các phương thức và thuộc tính mà lớp cha của nó đã khai báo (và cho phép).
Vậy còn đặc tính phủ quyết? Đặc tính này cho phép lớp con có thể khai báo các phương thức trùng với các phương thức của lớp cha, rồi sau đó lớp con đó sẽ định nghĩa lại nội dung của phương thức đó. Nó mang ý nghĩa “không chấp nhận” phương thức đó của lớp cha. Chính vì ý nghĩa này mà người ta hay gọi là phủ quyết (hay ghi đè, hay overriding) là vậy.
Như đã nói ở trên, để thực hiện cho nhu cầu overriding, bạn chỉ cần đặt tên phương thức ở lớp con trùng tên và tham số truyền vào với phương thức đã có ở lớp cha. Sau đó, tuy không bắt buộc, nhưng bạn nên khai báo một annotation (đừng dịch annotation ra tiếng Việt) là @Override ở mỗi phương thức overriding để có sự rõ ràng về code. Với phương thức đã override này ở lớp con, bạn có thể thiết kế lại logic bên trong nó, một khi ở nơi nào đó gọi đến phương thức đó của lớp con, thay vì sử dụng phương thức của lớp cha theo nguyên lý kế thừa, thì phương thức của lớp con được sử dụng.
Chúng ta cùng xem ví dụ sau. Ví dụ sẽ sử dụng hai lớp HinhTron và HinhTru, trong đó HinhTru kế thừa từ HinhTron. Chúng ta hãy thử khai báo phương thức xuatThongTin() ở cả hai lớp, theo sơ đồ lớp sau.

Code của chúng cũng đơn giản như sau.
Với việc khai báo hai lớp như trên, thì bạn xem cách gọi đến chúng ở main() như thế nào nhé.
Nếu thực thi chương trình, bạn sẽ thấy hai lệnh xuatThongTin() ở hai lớp sẽ in ra console như sau.
Đó là bởi vì xuatThongTin() ở HinhTron đã bị override (hay bị phủ quyết, ghi đè) bởi xuatThongTin() ở HinhTru. Nếu bạn thử nghiệm bằng cách xóa xuatThongTin() ở HinhTru đi, bạn sẽ nhận được hai dòng in ra console như nhau, đó là bởi vì nếu không có sự override này, thì đặc tính dùng lại của kế thừa sẽ được tận dụng. Bạn đã hiểu khái niệm overriding rồi đúng không nào.
Có một ý mở rộng cho bạn. Đó là nếu bạn muốn phương thức overriding có dùng đến phương thức trùng nhau đó của lớp cha, thì bạn cứ mạnh dạn sử dụng từ khóa super như ví dụ dưới đây nhé.
Lý thuyết của overriding chỉ có như trên đây thôi. Dễ nhớ đúng không nào.
Giờ thì chúng ta thử tạo ra một mối quan hệ hoàn chỉnh giữa hai lớp HinhTron và HinhTru. Tất nhiên vẫn là quan hệ kế thừa, nhưng bạn sẽ thấy đầy đủ hai tính năng dùng lại và phủ quyết.
Bạn có thể tạo project mới cho bài thực hành này.
Chúng ta cùng nhìn qua sơ đồ lớp sau.

Ở lớp HinhTron, chúng ta không cần phương thức kêu người dùng nhập bán kính từ console như các bài thực hành trước nữa, mà hãy truyền bán kính này vào constructor luôn cho nó lẹ. Các phương thức tinhChuVi() và tinhDienTich() đều trả kết quả tính toán dựa vào bán kính có được từ constructor. Và cuối cùng, phương thức xuatThongTin() sẽ như ví dụ trên kia, phương thức này sẽ bị override bởi lớp HinhTru kế thừa sau đó.
Ở lớp Hinhtru, lớp này kế thừa các thuộc tính PI và banKinh, các phương thức tinhChuVi() và tinhDienTich(). HinhTru khai báo thêm thuộc tính chieuCao và phương thức tinhTheTich(). Và cuối cùng, phương thức xuatThongTin() sẽ override lại so với lớp cha.
Xong. Bây giờ thì chúng ta sẽ sử dụng chúng ở main().
Và đây là kết quả khởi chạy chương trình.
Chưa có câu hỏi nào cho bài này.