Java Bài 22: Từ Khóa this & Từ Khóa super

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ứ 22 trong chuỗi bài học về lập trình ngôn ngữ Java của Yellow Code Books.

Vậy là bạn đã vừa mới làm quen với kế thừa trong Java từ bài học hôm trước, qua đó bạn đã biết làm thế nào để khai báo một mối quan hệ kế thừa, khi nào nên kế thừa, và đặc tính thừa kế lại các giá trị từ lớp cha cho lớp con là như thế nào.

Sang đến bài học hôm nay, chúng ta đành tạm khoan hãy nói về tính phủ quyết trong kế thừa, mà hãy xem định nghĩa và cách sử dụng của hai loại từ khóa thissuper. Chúng khá quan trọng, nhưng nếu nói sớm quá thì không được, vì chúng có liên quan đến tính kế thừa, mà nói trễ quá thì các bạn sẽ không thể hiểu được một số chỗ cần sử dụng chúng.

Nào chúng ta hãy bắt đầu.

Từ Khóa this

Chắc các bạn còn nhớ. Từ khóa this này đã được mình dùng đến ở bài 17 rồi, là khi bạn muốn phân biệt đâu là biến còn đâu là thuộc tính của lớp, nếu chúng có trùng tên với nhau. Và ở bài học đó mình cũng chưa nói rõ về this lắm. Mục này mình sẽ giúp bạn liệt kê tất cả các công dụng mà this mang lại.

Như bạn đã biết, từ khóa this mang ý nghĩa là: chính là đối tượng này. Nó tham chiếu ngược lại đến đối tượng, giúp bạn có thể truy xuất đến các giá trị của đối tượng đó. Sau đây là một vài vai trò hữu ích của this, bạn sẽ còn được thấy this được sử dụng khá nhiều trong các bài học Java về sau, và cả trong lập trình Android nữa.

Sử Dụng this Khi Truy Xuất Đến Thuộc Tính Và Phương Thức Trong Lớp

Như bạn có làm quen từ bài 17, bạn đã dùng đến this để truy xuất đến thuộc tính của một lớp, thì vai trò của this cũng sẽ tương tự khi bạn dùng để truy xuất đến phương thức.

Mục đích chính của việc sử dụng this khi này như bạn đã biết, đó là giúp phân biệt được đâu là biến và đâu là thuộc tính của lớp, khi chúng có cùng tên với nhau. Tuy nhiên, bạn hoàn toàn có thể sử dụng this ở mọi lúc mọi nơi khi muốn truy xuất đến các thuộc tính và phương thức này, như ví dụ dưới đây. Nhưng bạn cũng đừng nên sử dụng this tùy tiện quá, nó khiến cho code của chúng ta trở nên rườm rà, như ví dụ dưới hơi bị lạm dụng this, chỉ là mình muốn hiển thị chúng nhiều nhiều cho các bạn xem chơi thôi.

public class HinhTron {

	public float banKinh;

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

    public void tinhChuVi() {
        // Tính chu vi hình tròn và in ra console
    }

    public void tinhDienTich() {
        // Tính diện tích hình tròn và in ra console
    }

    public void inHinhTron() {
    	System.out.println("Hình tròn bán kính = " + this.banKinh);
    	this.tinhChuVi();
    	this.tinhDienTich();
    }
}

Sử Dụng this Khi Gọi Đến Một Constructor Khác Bên Trong Lớp

Theo lẽ ở bài về constructor mình nên nói luôn về cách sử dụng này của this, nhưng mình cố tình để đến bài hôm nay sẽ nói chung một lượt.

Nếu như trong một lớp của bạn có nhiều constructor, và bạn muốn một constructor nào đó gọi đến một constructor khác, thường thì kiểu gọi này giúp các constructor tận dụng lại được các code khởi tạo của nhau, tránh việc viết lại.

this được dùng đến trong trường hợp này hơi khác trường hợp trên một chút, đó là bạn phải gọi với tham số truyền vào như này this([tham_số_truyền_vào]), như khi bạn gọi đến một phương thức vậy. Mình gọi tắt thành this() cho dễ trình bày.

Lúc này, khi gọi đến, this() sẽ gọi đến một constructor nào đó, tùy vào tham_số_truyền_vào cho nó mà constructor tương ứng được gọi. Nhưng việc gọi đến một constructor khác này không tạo ra một lớp mới, mà chỉ là tận dụng lại constructor như khi bạn gọi hàm nào đó bên trong một class mà thôi. Bạn xem ví dụ.

public class HinhChuNhat extends HinhHoc {

    public float dai;
    public float rong;

    // Constructor
    public HinhChuNhat() {
        ten = "Hình Chữ Nhật";
    }

    // Constructor
    public HinhChuNhat(float dai, float rong) {
        this(); // Gọi đến HinhChuNhat()
        this.dai = dai;
        this.rong = rong;
    }

    // Constructor
    public HinhChuNhat(float canh) {
        this(canh, canh); // Gọi đến HinhChuNhat(dai, rong)
    }

    public void tinhChuVi() {
        chuVi = 2 * (dai + rong);
    }

    public void tinhDienTich() {
        dienTich = dai * rong;
    }

}

Có một điều lưu ý rằng, từ khóa this() dùng trong trường hợp này chỉ được dùng trong các constructor, để mà tận dụng lại các constructor khác như ví dụ trên, nếu bạn để this() này vào các phương thức bình thường khác, sẽ có báo lỗi xảy ra từ hệ thống. Thêm một điều nữa, là this() nếu có, thì nó phải là dòng code đầu tiên bên trong một constructor, trường hợp dưới đây là sai, hệ thống sẽ báo lỗi vì trước this() có các dòng code khác.

// Constructor
public HinhChuNhat(float dai, float rong) {
    this.dai = dai;
    this.rong = rong;
    this();
}

Sử Dụng this Làm Tham Số Truyền Vào Một Phương Thức Hay Một Constructor Khác

Về phần này thì qua đến lập trình Android, bạn sẽ dùng đến nhiều hơn. Còn với khuôn khổ bên bài học Java này, sẽ hơi khó tìm thấy một ví dụ thực tế nào hay ho, do đó mình mượn code trên mạng về cho bạn thấy cách sử dụng từ khóa this cho mục đích tham số lần này. Bạn cũng sẽ thấy dễ hiểu thôi.

Ví dụ sau là dành cho việc sử dụng this làm tham số truyền vào một phương thức. Trường hợp cũng tương tự cho việc sử dụng this để làm tham số truyền vào một constructor. Nhìn vào ví dụ, ở trong lớp Bar, lớp Foo được khai báo làm tham số cho hàm barMethod() của lớp Bar. Sau đó ở lớp Foo, chỗ dòng được tô sáng, lớp này chỉ cần truyền vào barMethod() thông qua từ khóa this như vậy giúp lớp Bar được phép sử dụng Foo là một lớp đã được khai báo hoàn chỉnh, tức là bạn không cần sử dụng từ khóa new để tạo ra một lớp Foo nào nữa.

public class Foo {
    public void useBarMethod() {
        Bar theBar = new Bar();
        theBar.barMethod(this);
    }

    public String getName() {
        return "Foo";
    }
}

public class Bar {
    public void barMethod(Foo obj) {
        obj.getName();
    }
}

Sử Dụng this Là Một Thể Hiện Của Kết Quả Trả Về

Trường hợp trả về một kết quả là this, kết quả trả về chính thể hiện của lớp đó. Công dụng này của this có thể làm bạn, và cả mình nữa, cảm thấy bối rối. Bạn hãy nhìn vào ví dụ sau trước khi xem điều bối rối mà mình muốn nói đến là gì nhé, và vì sự bối rối này mà bạn có thể không cần dùng đến this trong trường hợp này ở ngoài thực tế. Nếu bạn nào biết rõ this trong trường hợp này thực sự mang ý nghĩa ra sao, để giúp mọi người bớt bối rối, thì hãy để lại comment cho mình nhé.

Đầu tiên mình có một lớp Student. Bạn thấy ở phương thức getStudent(), lớp này trả ra ngoài từ khóa this.

public class Student {

    public String name;
    public String age;

    // Constructor
    public Student(String name, String age) {
        this.name = name;
        this.age = age;
    }

    public Student getStudent() {
        return this;
    }
}

Sau đó, ở hàm main() có khai báo lớp Student, bạn xem main() sử dụng đến hàm getStudent() như thế nào nhé.

public class MainClass {

    public static void main(String[] args) {
        Student student = new Student("Yellow", "20");

        System.out.println("Name: " + student.getStudent().name);
        System.out.println("Age: " + student.getStudent().age);
    }

}

Thực ra hàm getStudent() trong lớp Student giúp trả về this, chính là thể hiện hiện tại của lớp, và vì vậy khi bạn gọi đến student.getStudent().name, thực chất vẫn là đang truy xuất đến thuộc tính name của Student, và vì vậy kết quả in ra console của code trên sẽ lần lượt là “Yellow”“20”. Bối rối thật sự xảy ra, khi bạn hoàn toàn có thể in ra kết quả như trên mà không cần đến getStudent() như sau. Bạn hãy chú ý sự khác biệt giữa hai hàm main().

public class MainClass {

    public static void main(String[] args) {
        Student student = new Student("Yellow", "20");

        System.out.println("Name: " + student.name);
        System.out.println("Age: " + student.age);
    }

}

Từ Khóa super

Khác với từ khóa this giúp tham chiếu đến chính đối tượng hiện tại. Từ khóa super lại giúp tham chiếu đến lớp cha, mà là lớp cha gần nhất của nó. Tức là bạn có thể hiểu vui là super không thể được sử dụng để tham chiếu đến lớp “ông nội” được nhé.

Dưới đây tiếp tục là một vài vai trò của super, bạn có thể so sánh với vai trò của this trên kia để xem sự khác biệt, và để dễ nhớ nữa.

Sử Dụng super Khi Truy Xuất Đến Thuộc Tính Và Phương Thức Của Lớp Cha Gần Nhất

Nếu như mục đích đầu tiên của this trên kia là để phân biệt đâu là biến và đâu là thuộc tính khi chúng nó bị trùng tên trong một lớp. Thì mục đích đầu tiên của super này là để phân biệt đâu là giá trị của lớp con và đâu là giá trị của lớp cha gần nhất khi chúng bị trùng tên. Sự trùng tên giữa phương thức của lớp con và lớp cha xảy ra nhiều hơn là trùng tên giữa các thuộc tính, và sự trùng tên này là có chủ đích, bạn có thể xem trước bài học về tính phủ quyết để hiểu rõ hơn vì sao có sự trùng tên này.

Ví dụ sau cho thấy lớp HinhTron định nghĩa trùng phương thức xuatTen() với cha của nó là HinhHoc, và rồi super xuất hiện ở xuatTen() của HinhTron giúp nó gọi đến xuatTen() ở cha trước, rồi mới đến các code còn lại bên trong xuatTen() của HinhTron, bạn có thể chạy thử code bên dưới.

public class HinhHoc {

    public String ten;

    public void xuatTen() {
        System.out.println("\n\n===== " + ten + " =====");
    }

    // Các phương thức khác
    // ...
}

public class HinhTron extends HinhHoc {

    // Constructor
    public HinhTron() {
        ten = "Hình Tròn";
    }

    public void xuatTen() {
        super.xuatTen();
        System.out.println("\nHàm xuất tên từ HinhTron");
    }

    // Các phương thức khác
    // ...
}

public class MainClass {

    public static void main(String[] args) {
        // Thử nghiệm với lớp Hình tròn
        HinhTron hinhTron = new HinhTron();
        hinhTron.xuatTen();
    }

}

Sử Dụng super Khi Gọi Đến Một Constructor Của Lớp Cha Gần Nhất

Cũng tương tự như khi bạn dùng this([tham_số_truyền_vào]), super([tham_số_truyền_vào]) giúp bạn gọi đến, và tận dụng lại các code khởi tạo bên lớp cha gần nhất.

Và cũng giống với từ khóa this(), super() chỉ được dùng trong các constructor, nếu bạn để super() này vào các phương thức bình thường khác, sẽ có báo lỗi xảy ra từ hệ thống. Và tương tự, từ khóa super() nếu có, thì nó phải được khai báo đầu tiên bên trong một constructor.

public class HinhHoc {

    public String ten;

    // Constructor
    public HinhHoc(String ten) {
        this.ten = ten;
    }

    // Các phương thức khác
    // ...
}

public class HinhTron extends HinhHoc {

    // Constructor
    public HinhTron() {
        super("Hình Tròn");
    }

    // Các phương thức khác
    // ...
}

Như vậy là chúng ta vừa xem xong các khái niệm và trường hợp sử dụng của hai từ khóa thissuper. Chúng khá quan trọng và xuất hiện nhiều trong các project hay các ví dụ của chúng ta từ bây giờ, các bạn hãy từ từ nắm vững và thực tập vận dụng chúng nhé. Tất nhiên bài hôm nay chỉ có lý thuyết, các thực hành liên quan đến hai từ khóa này sẽ xuất hiện ở các bài học sau.

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 lại tiếp tục nói về kế thừa, nhưng bài học sau sẽ nói về những lớp con “không ngoan ngoãn”, không giống như các lớp con của bài học làm quen với kế thừa hôm trước, những lớp con của bài sau không hoàn toàn kế thừa các phương thức từ lớp cha, mà Phủ quyết nó.

Advertisements
Rating: 4.2/5. From 5 votes.
Please wait...

Gửi phản hồi