Java Bài 27: Phương Thức Getter Và Setter

Posted by

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

Bài học hôm nay chúng ta cùng xem qua cách tổ chức các phương thức gettersetter trong Java. Hai phương thức này là gì và chúng có ý nghĩa như thế nào trong việc tổ chức các mối quan hệ giữa các đối tượng trong OOP? Mời các bạn cùng đọc tiếp nhé.

Getter Và Setter Là Gì?

Đây là hai tên gọi của hai thể loại phương thức. Chúng có liên quan đến đặc tính gói ghém dữ liệu trong lập trình hướng đối tượng, tiếng Anh gọi đặc tính này là Encapsulation. Trước đó, các định nghĩa về khả năng truy cập cũng là một dạng của gói ghém dữ liệu.

Vậy thì gói ghém dữ liệu là gì? Tính gói ghém dữ liệu là một trong những đặc tính cơ bản trong kế thừa, bên cạnh các đặc tính khác, như tính kế thừa (Inheritance) mà bạn đã biết, tính đa hình (Polymorphism) và tính trừu tượng (Abstraction) mà bạn sẽ làm quen sau. Đặc tính gói ghém dữ liệu giúp bao bọc lấy các thuộc tính của một lớp. Nó làm cho các thuộc tính của lớp bị ẩn đi so với các lớp khác.

Vậy getter, setter liên quan gì đến gói ghém dữ liệu? Các phương thức này giúp cho các thuộc tính private của một lớp hoặc sẽ không thể truy xuất, hoặc chỉ được phép cho đọc (thông qua phương thức getter), hoặc chỉ cho ghi (thông qua phương thức setter) từ các lớp bên ngoài. Đó gọi là gói ghém dữ liệu.

Định nghĩa trên chắc đủ rõ ràng cho bạn để hiểu loại phương thức này đúng không. Nếu chưa thực sự hiểu về nó thì mời bạn đọc tiếp các phần sau nhé.

Tổ Chức Các Phương Thức Getter Và Setter Như Thế Nào?

Để tạo ra sự gói ghém cho các thuộc tính trong một lớp thông qua gettersetter, bạn làm theo các bước sau.

– Định nghĩa khả năng truy cập cho các thuộc tính trong một lớp về private.
– Xây dựng phương thức getter hay setter cho từng thuộc tính private đó. Với setter bạn nên đặt tên phương thức là setTênThuộcTính(). Còn với getter thì bạn nên đặt tên phương thức là getTênThuộcTính(). Việc xuất hiện của gettersetter cho các thuộc tính private là không bắt buộc. Bạn có thể không cần khai báo bất cứ getter hay setter nào cả nếu như bạn không muốn thuộc tính đó được thấy bởi bất kỳ lớp nào. Hoặc bạn chỉ cần xây dựng getter cho một thuộc tính private nếu bạn muốn thuộc tính đó chỉ được xem, không được sửa chữa. Hoặc chỉ xây dựng setter cho một thuộc tính private nếu bạn muốn thuộc tính đó chỉ có khả năng chỉnh sửa từ bên ngoài mà không được xem.

Nào, để dễ hiểu một chút, chúng ta cùng xem ví dụ ở lớp ToaDo, bạn xem, các thuộc tính x, y ở lớp này được chỉ định private, rồi sau đó nó phải xây dựng thêm các phương thức gettersetter ở bên dưới khai báo thuộc tính.

public class ToaDo {

	private int x;
	private int y;
	
	// getter của thuộc tính x
	public int getX() {
		return x;
	}
	
	// setter của thuộc tính x
	public void setX(int x) {
		this.x = x;
	}
	
	
	// getter của thuộc tính y
	public int getY() {
		return y;
	}
	
	// setter của thuộc tính y
	public void setY(int y) {
		this.y = y;
	}
	
}

Ngay cả với lớp HinhTron hay các lớp hình học khác mà bạn đã biết, chúng ta cũng có thể xây dựng gettersetter cho chúng.

class HinhTron {
	
	private final float PI = 3.14f;
	private float banKinh;
	
	public float getBanKinh() {
		return banKinh;
	}
	
	public void setBanKinh(float banKinh) {
		this.banKinh = banKinh;
	}
	
    public float tinhChuVi() {
        return 2 * PI * banKinh;
    }
 
    public float tinhDienTich() {
        return PI * banKinh * banKinh;
    }
}

Vậy khi gọi đến các phương thức gettersetter này từ bên ngoài lớp sẽ như thế nào. Bạn hãy chú ý vào hàm main() sau đây. Với ví dụ này, mình đã thay thế việc set bán kính hình tròn thông qua constructor, bằng việc gọi đến setter của lớp.

public class MainClass {

	public static void main(String[] args) {
		// Khởi tạo đối tượng hinhTron từ lớp HinhTron
		HinhTron hinhTron = new HinhTron();
		
		// set bán kính cho hinhTron thông qua phương thức setter
		hinhTron.setBanKinh(10);
		
		// Các tính toán khác
		float chuVi = hinhTron.tinhChuVi();
		float dienTich = hinhTron.tinhDienTich();
		System.out.println("Chu vi hình tròn: " + chuVi + "; Và diện tích: " + dienTich) ;
	}
}

Dùng Eclipse Để Khai Báo Getter Và Setter

Bạn có thể thấy rằng, việc đặt tên cho gettersetter của một thuộc tính lớp không có một nguyên tắc cụ thể, chúng nên là getXXX()setXXX(). Vậy thì loạn quá đúng không nào. Tuy nhiên, Eclipse, hay sau này bạn lập trình Android với Android Studio, cũng đều hỗ trợ chúng ta một công cụ tự động phát sinh code gettersetter cho thuộc tính.

Bạn hãy cùng mình thử nghiệm tính năng sinh code tự động qua các bước sau. Lưu ý là nếu bạn thử với Android Studio, hay với các công cụ lập trình Java nào khác, thì mình nghĩ là sẽ tương tự nhau đấy.

Đầu tiên bạn có thể dùng lớp ToaDo, hoặc tạo một lớp bất kỳ trong Eclipse. Bạn hãy khai báo một hay nhiều thuộc tính private cho nó, như sau.

Getter và Setter - Khai báo thuộc tính private

Bạn có thể thấy các icon cảnh báo màu vàng bên thanh trái của editor, tương ứng cho từng thuộc tính private. Vì hệ thống thấy rằng chúng ta khai báo private cho thuộc tính, mà không sử dụng chúng ở bên trong lớp, thì hiển nhiên là có vấn đề rồi, vì bên ngoài lớp có nhìn thấy các giá trị này đâu.

Khi này bạn cứ click vào một trong các cảnh báo này, một dialog nhỏ xuất hiện, bạn hãy chọn vào mục Create getter and setter for ‘tên_thuộc_tính’…. Với dialog xuất hiện tiếp theo sau lựa chọn này, bạn cứ để mặc định rồi nhấn OK là xong.

Getter và Setter - Eclipse gợi ý Getter và Setter

Bạn có thể tự kiểm chứng. Kết quả của tùy chọn này giúp bạn tạo ra cả gettersetter cho thuộc tính tương ứng. Bạn cứ làm lần lượt cho các thuộc tính còn lại nhé. Và nếu như bạn muốn bỏ một getter hay một setter nào đó, thì cứ xóa đi phương thức đó sau khi tạo bởi các bước trên đây.

Getter, Setter Và Những Tùy Biến

Đến đây ắt hẳn bạn đã hiểu gettersetter là gì rồi. Nhưng bạn biết không, các phương thức gettersetter này không hẳn là được dùng để get hat set dữ liệu vào cho thuộc tính của lớp đâu. Bởi vì chúng là các phương thức, nên bạn hoàn toàn có thể tận dụng chúng để làm một số thao tác trước khi get, hay set giá trị.

Mình ví dụ một lớp SinhVien, với đủ thứ các ràng buộc về việc nhập/xuất tên và tuổi như sau. Bạn chú ý để trắc nghiệm kết quả trả về nhé.

public class SinhVien {

	private String ten;
	private String tuoi;
	
	public String getTen() {
		return ten;
	}
	
	public void setTen(String ten) {
		if (ten == null || ten.isEmpty()) {
			// Nếu biến ten chưa khởi tạo (mang giá trị null), hoặc biến ten có nội dung rỗng
			// Thì hãy lưu với tên là "Không biết"
			this.ten = "Không biết";
		} else {
			this.ten = ten;
		}
	}

	public String getTuoi() {
		if (Integer.valueOf(tuoi) != -1) {
			// Tuổi hợp lệ
			return tuoi;
		} else {
			return "Tuổi không hợp lệ";
		}
	}

	public void setTuoi(int tuoi) {
		// Kiểm tra tuổi có hợp lý hay không, nếu hợp lý thì lưu vào, nếu không sẽ tìm cách báo lỗi bằng cách lưu giá trị âm
		if (tuoi > 18) {
			this.tuoi = String.valueOf(tuoi);
		} else {
			this.tuoi = String.valueOf(-1);
		}
	}

}

Với khai báo lớp như trên. Giả sử mình khởi tạo các đối tượng sinh viên này ở hàm main() như sau.

public class MainClass {

	public static void main(String[] args) {
		// Khởi tạo các đối tượng sinhVien
		SinhVien sinhVien1 = new SinhVien();
		SinhVien sinhVien2 = new SinhVien();
		
		// Set thông tin vào cho các sinh viên
		sinhVien1.setTen(""); // Tên không có
		sinhVien1.setTuoi(23);
		
		sinhVien2.setTen("Peter");
		sinhVien2.setTuoi(17);
		
		// In thông tin các sinh viên
		System.out.println("Sinh viên 1 có tên: " + sinhVien1.getTen() + ", tuổi: " + sinhVien1.getTuoi());
		System.out.println("Sinh viên 2 có tên: " + sinhVien2.getTen() + ", tuổi: " + sinhVien2.getTuoi());
	}
}

Bạn thử đoán xem kết quả in ra console sẽ như thế nào. Sau đây là kết quả.

Sinh viên 1 có tên: Không biết, tuổi: 23
Sinh viên 2 có tên: Peter, tuổi: Tuổi không hợp lệ

Vậy là chúng ta vừa xem qua kiến thức về xây dựng các phương thức gettersetter trong hướng đối tượng. Thực sự theo mình thấy, thì gettersetter không cao siêu gì đâu. Chắc chắn khi nhìn vào các ví dụ trên, bạn có thể tự hỏi rằng, hai phương thức này liệu có quan trọng không. Thực ra, cũng đã có nhiều tranh cãi rằng liệu có nên xây dựng gettersetter cho các thuộc tính của lớp trong Java không, hay cứ khai báo public hết cho tất cả các thuộc tính. Vì nhìn chung lại gettersetter sẽ làm cho các lớp trở nên rườm rà hơn. Có một số ý kiến cho rằng hiệu năng của ứng dụng cũng giảm đi với gettersetter. Nhưng như bài học có nói đến, rằng đây là một trong những cách gói ghém dữ liệu của lớp. Với gettersetter, bạn sẽ có thể chỉ định được thuộc tính đó hoàn toàn ẩn, hay chỉ đọc, hay chỉ ghi, hay có thể đọc và ghi. Một cách để làm cho code bạn được tường minh hơn.

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

Bài tiếp theo đây chúng ta sẽ tìm hiểu về từ khóa static bên trong một lớp nhé.

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

2 comments

  1. Cảm ơn anh vì hướng dẫn rất cụ thể.
    Em muốn hỏi là trong ví dụ SinhVien trên, sao thuộc tính tuổi lại có kiểu giá trị là String, rồi khi mình dùng hàm if lại so sánh tuổi > 18 được ạ?

    1. Chào bạn Thảo. Thực ra thì thuộc tính tuổi nên dùng int hay hơn, nhưng với ví dụ này mình muốn làm cho nó “mạo hiểm” hơn, bằng cách khai báo tuổi là String. Khi xem lại mình đồng ý với bạn dùng kiểu int sẽ dễ hiểu hơn, có lẽ mình sẽ sửa lại nội dung ví dụ này. Tuy nhiên, bạn có thấy ở phương thức setTuoi(int), mình đã dùng kiểu int để so sánh tuổi > 18 trước khi chuyển đổi từ int sang String bằng cách gọi String.valueOf(tuoi), bạn xem lại nha.

Gửi phản hồi