Java Bài 23: Tính Phủ Quyết (Overriding) Trong Kế Thừa

Posted by

Được chỉnh sửa ngày 2/8/2017.

Chào mừng các bạn đến với bài học Java thứ 23 trong chuỗi bài học về lập trình ngôn ngữ Java của Yellow Code Books.

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, nó là sự Phủ quyết (Overriding).

Tính Phủ Quyết (Overriding) Là gì?

Tính Phủ quyết cũng là một đặc tính của Kế thừa. 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.

Phủ Quyết (Overriding) Như Thế Nào?

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 chú thích (tiếng Anh gọi là Annotation) với nội dung @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 HinhTronHinhTru, 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.

hinhtron_hinhtru

Code của chúng cũng đơn giản như sau.

public class HinhTron {

	public void xuatThongTin() {
		System.out.println("Đây là Hình tròn");
	}
}
public class HinhTru extends HinhTron {

	@Override
	public void xuatThongTin() {
		System.out.println("Đây là Hình trụ");
	}
}

Với việc khai báo hai lớp như trên, thì bạn xem cách gọi đến chúng ở hàm main() như thế nào nhé.

public class MainClass {

	public static void main(String[] args) {
		HinhTron hinhTron = new HinhTron();
		HinhTru hinhTru = new HinhTru();

		hinhTron.xuatThongTin();
		hinhTru.xuatThongTin();
	}
}

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.

Screen Shot 2017-07-26 at 17.11.51

Đó 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é.

public class HinhTru extends HinhTron {

	@Override
	public void xuatThongTin() {
		super.xuatThongTin();
		System.out.println("Đây là Hình trụ");
	}
}

Thực Hành Kế Thừa (Có Tính Phủ Quyết)

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 HinhTronHinhTru. 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ạiPhủ 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.

hinhtron_hinhtru_thuchanh

Ở 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()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 đó.

public class HinhTron {

	public final float PI = 3.14f;

	public float banKinh;

    // Constructor
    public HinhTron(float banKinh) {
        this.banKinh = banKinh;
    }

    public float tinhChuVi() {
        return 2 * PI * banKinh;
    }

    public float tinhDienTich() {
        return PI * banKinh * banKinh;
    }

	public void xuatThongTin() {
		System.out.println("Đây là Hình tròn");
		System.out.println("Hình tròn có Chu vi: " + tinhChuVi() + " và Diện tích: " + tinhDienTich());
	}
}

Ở lớp Hinhtru, lớp này kế thừa các thuộc tính PIbanKinh, các phương thức tinhChuVi()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.

public class HinhTru extends HinhTron {

	public float chieuCao;

	// Constructor
	public HinhTru(float banKinh, float chieuCao) {
		super(banKinh);
		this.chieuCao = chieuCao;
	}

	public float tinhTheTich() {
		return tinhDienTich() * chieuCao;
    }

	@Override
	public void xuatThongTin() {
		System.out.println("Đây là Hình trụ");
		System.out.println("Hình trụ có Thể tích: " + tinhTheTich());
	}
}

Xong. Bây giờ thì chúng ta sẽ sử dụng chúng ở hàm main().

public class MainClass {

	public static void main(String[] args) {
		HinhTron hinhTron = new HinhTron(10);
		HinhTru hinhTru = new HinhTru(10, 20);

		hinhTron.xuatThongTin();
		hinhTru.xuatThongTin();
	}
}

Và đây là kết quả khởi chạy chương trình.

Screen Shot 2017-07-26 at 17.50.38

Chúng ta vừa xem qua một đặc tính nữa trong chuỗi kiến thức về OOP. Với kiến thức về overriding này, mình xem nó như một phần của sự Kế thừa, nó nói về tính Phủ quyết, bên cạnh tính Dùng lại mà chúng ta đã làm quen từ bài học trước.

Cảm ơn bạn đã đọc các bài viết của Yellow Code Books. Bạn hãy đánh giá 5 sao nếu thấy thích bài viết, hãy comment bên dưới nếu có thắc mắc, hãy để lại địa chỉ email của bạn để nhận được thông báo mới nhất khi có bài viết mới, và nhớ chia sẻ các bài viết của Yellow Code Books đến nhiều người khác nữa nhé.

Bài Kế Tiếp

Chúng ta sẽ tìm hiểu về lớp Object, để xem lớp này có vai trò gì trong chuỗi kiến thức về OOP của chúng ta nhé.

Advertisements
Rating: 5.0/5. From 7 votes.
Please wait...

Gửi phản hồi