JavaRush /Blog Java /Random-VI /Getters và setters

Getters và setters

Xuất bản trong nhóm
Xin chào! Trong các bài giảng trước, bạn đã học cách tạo các lớp chính thức của riêng mình với các trường và phương thức. Đây là sự tiến bộ nghiêm túc, được thực hiện tốt! Nhưng bây giờ tôi phải nói với bạn một sự thật khó chịu. Chúng tôi đã không tạo ra các lớp học của mình một cách hoàn hảo! Tại sao? Thoạt nhìn, không có lỗi nào trong lớp này:
public class Cat {

   public String name;
   public int age;
   public int weight;

   public Cat(String name, int age, int weight) {
       this.name = name;
       this.age = age;
       this.weight = weight;
   }

   public Cat() {
   }

   public void sayMeow() {
       System.out.println("Meow!");
   }
}
Trong thực tế, có. Hãy tưởng tượng rằng trong khi ngồi làm việc bạn đã viết một lớp như thế này Cat, biểu thị những con mèo. Và anh ấy đã về nhà. Trong khi bạn đi vắng, một lập trình viên khác đến làm việc, tạo ra lớp của riêng anh ta Main, nơi anh ta bắt đầu sử dụng lớp mà bạn đã viết Cat.
public class Main {

   public static void main(String[] args) {

       Cat cat = new Cat();
       cat.name = "";
       cat.age = -1000;
       cat.weight = 0;
   }
}
Không quan trọng tại sao anh ta làm điều đó hay nó xảy ra như thế nào: có thể người đó mệt hoặc không ngủ đủ giấc. Một điều quan trọng nữa: lớp hiện tại của chúng tôi Catcho phép bạn gán các giá trị điên rồ cho các trường. Kết quả là chương trình chứa các đối tượng có trạng thái không chính xác, chẳng hạn như con mèo có độ tuổi -1000 năm này. Cuối cùng chúng ta đã phạm phải sai lầm gì? Khi tạo lớp, chúng tôi đã hiển thị dữ liệu của nó. Fields namevà thuộc phạm vi công cộng age. weightChúng có thể được truy cập ở bất kỳ đâu trong chương trình: chỉ cần tạo một đối tượng Cat- và thế là xong, bất kỳ lập trình viên nào cũng có thể truy cập trực tiếp vào dữ liệu của nó thông qua toán tử “ .
Cat cat = new Cat();
cat.name = "";
Ở đây chúng ta truy cập trực tiếp vào trường namevà đặt giá trị của nó. Chúng ta cần bằng cách nào đó bảo vệ dữ liệu của mình khỏi sự can thiệp không chính xác từ bên ngoài. Điều gì là cần thiết cho việc này? Đầu tiên, tất cả các biến thể hiện (trường) phải được đánh dấu bằng công cụ sửa đổi private. Riêng tư là công cụ sửa đổi quyền truy cập nghiêm ngặt nhất trong Java. Nếu bạn sử dụng nó, các trường của lớp Catsẽ không thể truy cập được bên ngoài nó.
public class Cat {

   private String name;
   private int age;
   private int weight;

   public Cat(String name, int age, int weight) {
       this.name = name;
       this.age = age;
       this.weight = weight;
   }

   public Cat() {
   }

   public void sayMeow() {
       System.out.println("Meow!");
   }
}

public class Main {

   public static void main(String[] args) {

       Cat cat = new Cat();
       cat.name = "";//error! The name field in the Cat class has private access!
   }
}
Trình biên dịch nhìn thấy điều này và ngay lập tức tạo ra lỗi. Bây giờ các lĩnh vực dường như được bảo vệ. Nhưng hóa ra quyền truy cập vào chúng bị đóng "chặt chẽ": chương trình thậm chí không thể đạt được trọng lượng của một con mèo hiện có, nếu cần. Đây cũng không phải là một lựa chọn: ở dạng này, lớp của chúng tôi thực tế không thể sử dụng được. Lý tưởng nhất là chúng ta cần cho phép một số loại quyền truy cập hạn chế vào dữ liệu:
  • Các lập trình viên khác sẽ có thể tạo các đối tượngCat
  • Họ có thể đọc dữ liệu từ các đối tượng đã tồn tại (ví dụ: lấy tên hoặc tuổi của một con mèo đã tồn tại)
  • Cũng có thể gán giá trị trường. Nhưng đồng thời - chỉ có giá trị đúng. Các đối tượng của chúng ta phải được bảo vệ khỏi những đối tượng không chính xác (không có “tuổi = -1000 năm” và những thứ tương tự).
Danh sách các yêu cầu là khá! Nhưng trên thực tế, tất cả điều này có thể dễ dàng đạt được bằng các phương pháp đặc biệt - getterssetters .
Getters và setters - 2
Tên này xuất phát từ tiếng Anh “ get ” - “ receive ” (tức là “phương thức lấy giá trị của một trường”) và set - “ set ” (tức là “phương thức đặt giá trị của một trường”). Hãy xem chúng trông như thế nào khi sử dụng lớp của chúng ta làm ví dụ Cat:
public class Cat {

   private String name;
   private int age;
   private int weight;

   public Cat(String name, int age, int weight) {
       this.name = name;
       this.age = age;
       this.weight = weight;
   }

   public Cat() {
   }

   public void sayMeow() {
       System.out.println("Meow!");
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public int getAge() {
       return age;
   }

   public void setAge(int age) {
       this.age = age;
   }

   public int getWeight() {
       return weight;
   }

   public void setWeight(int weight) {
       this.weight = weight;
   }
}
Như bạn có thể thấy, mọi thứ khá đơn giản :) Tên của họ thường bao gồm từ get/set + tên trường mà họ chịu trách nhiệm. Ví dụ: một phương thức getWeight()trả về giá trị của một trường weightcho đối tượng mà nó được gọi. Đây là những gì nó trông giống như trong chương trình:
public class Main {

   public static void main(String[] args) {

       Cat barsik = new Cat("Barsik", 5, 4);
       String barsikName = barsik.getName();
       int barsikAge = barsik.getAge();
       int barsikWeight = barsik.getWeight();

       System.out.println("Cat name: " + barsikName);
       System.out.println("Cat's age: " + barsikAge);
       System.out.println("Weight of the cat: " + barsikWeight);
   }
}
Đầu ra của bảng điều khiển:

Name кота: Барсик
Возраст кота: 5
Вес кота: 4
Bây giờ từ một lớp khác ( Main) có quyền truy cập vào các trường Cat, nhưng chỉ thông qua getters . Xin lưu ý rằng getters có một công cụ sửa đổi truy cập public, nghĩa là chúng có thể truy cập được từ mọi nơi trong chương trình. Còn việc gán giá trị thì sao? Các phương thức setter chịu trách nhiệm cho việc này
public void setName(String name) {
   this.name = name;
}
Công việc của họ, như bạn có thể thấy, cũng đơn giản. Chúng ta gọi một phương thức setName()trên một đối tượng Cat, truyền cho nó một chuỗi làm đối số và chuỗi này được gán cho một trường namecủa đối tượng của chúng ta.
public class Main {

   public static void main(String[] args) {

       Cat barsik = new Cat("Barsik", 5, 4);

       System.out.println("The original name of the cat is " + barsik.getName());
       barsik.setName("Basil");
       System.out.println("The new name of the cat -" + barsik.getName());
   }
}
Ở đây chúng tôi sử dụng cả getters và setters. Đầu tiên, bằng cách sử dụng getter, chúng tôi đã nhận và xuất ra bảng điều khiển tên ban đầu của con mèo. Sau đó, bằng cách sử dụng setter, namemột giá trị mới được gán cho trường của nó - “Vasily”. Và sau đó, bằng cách sử dụng getter, chúng tôi lấy lại tên để kiểm tra xem nó có thực sự thay đổi hay không. Đầu ra của bảng điều khiển:

Изначальное Name кота — Барсик
Новое Name кота — Васorй
Có vẻ như, sự khác biệt là gì? Chúng ta cũng có thể gán giá trị không chính xác cho các trường đối tượng, ngay cả khi chúng ta có setters:
public class Main {

   public static void main(String[] args) {

       Cat barsik = new Cat("Barsik", 5, 4);
       barsik.setAge(-1000);

       System.out.println("Age of Barsik -" + barsik.getAge() + " years");
   }
}
Đầu ra của bảng điều khiển:

Возраст Барсика — -1000 лет
Sự khác biệt là setter là một phương thức chính thức . Và trong một phương thức, không giống như một trường, bạn có thể đặt logic xác minh mà bạn cần để ngăn các giá trị không được chấp nhận. Ví dụ: bạn có thể dễ dàng tắt việc gán số âm theo tuổi:
public void setAge(int age) {
   if (age >= 0) {
       this.age = age;
   } else {
       System.out.println("Error! Age cannot be negative!");
   }
}
Và bây giờ mã của chúng tôi hoạt động chính xác!
public class Main {

   public static void main(String[] args) {

       Cat barsik = new Cat("Barsik", 5, 4);
       barsik.setAge(-1000);

       System.out.println("Age of Barsik -" + barsik.getAge() + " years");
   }
}
Đầu ra của bảng điều khiển:

Ошибка! Возраст не может быть отрицательным числом!
Возраст Барсика — 5 лет
Có một hạn chế bên trong setter và nó bảo vệ khỏi những nỗ lực đặt dữ liệu không chính xác. Tuổi của Barsik vẫn không thay đổi. Getters và setters phải luôn được tạo. Ngay cả khi các trường của bạn không có hạn chế về các giá trị có thể có thì chúng cũng sẽ không gây hại gì. Hãy tưởng tượng một tình huống: bạn và đồng nghiệp của mình đang cùng nhau viết một chương trình. Bạn đã tạo một lớp Catvới các trường công khai và tất cả các lập trình viên đều sử dụng chúng theo ý muốn. Và rồi một ngày đẹp trời bạn chợt nhận ra: “Chết tiệt, sớm hay muộn ai đó cũng có thể vô tình gán số âm cho một biến weight! Chúng ta cần tạo setters và đặt tất cả các trường ở chế độ riêng tư!” Bạn tạo chúng và tất cả mã do đồng nghiệp của bạn viết sẽ ngay lập tức bị hỏng. Rốt cuộc, họ đã viết một loạt mã để truy cập Cattrực tiếp vào các trường.
cat.name = "Hippopotamus";
Và bây giờ các trường đã trở thành riêng tư và trình biên dịch tạo ra một loạt lỗi!
cat.name = "Hippopotamus";//error! The name field of the Cat class has private access!
Trong tình huống như vậy, sẽ tốt hơn nếu ẩn các trường và tạo getter-setters ngay từ đầu . Tất cả đồng nghiệp của bạn sẽ sử dụng chúng và nếu bạn nhận ra muộn rằng bạn cần giới hạn các giá trị trường, bạn chỉ cần thêm một dấu kiểm bên trong setter. Và không ai có thể phá vỡ mã đã được viết. Tất nhiên, nếu bạn muốn quyền truy cập chỉ đọc vào một trường nhất định, bạn có thể tạo một getter cho trường đó. “Bên ngoài”, tức là bên ngoài lớp của bạn, chỉ có thể truy cập được các phương thức. Dữ liệu phải được ẩn đi.
Getters và setters - 4
Một sự tương tự có thể được thực hiện với một chiếc điện thoại di động. Hãy tưởng tượng rằng thay vì bật một chiếc điện thoại di động thông thường, bạn được tặng một chiếc điện thoại có vỏ mở, trong đó có tất cả các dây, mạch điện, v.v. dính ra ngoài. Chiếc điện thoại này hoạt động: nếu bạn cố gắng và sử dụng các sơ đồ, bạn thậm chí có thể thực hiện được cuộc gọi. Nhưng có lẽ bạn sẽ phá vỡ nó. Thay vào đó, công ty sản xuất cung cấp cho bạn một giao diện: khách hàng chỉ cần quay số được yêu cầu, nhấn nút màu xanh lục trên điện thoại và cuộc gọi bắt đầu. Và anh ấy không quan tâm đến những gì đang diễn ra bên trong với các mạch điện và dây điện cũng như cách chúng thực hiện nhiệm vụ của mình. Trong ví dụ này, công ty đã hạn chế quyền truy cập vào “nội bộ” (dữ liệu) của điện thoại và chỉ để lại giao diện (phương thức) bên ngoài. Kết quả là khách hàng sẽ nhận được thứ mình muốn (gọi điện) và chắc chắn sẽ không làm vỡ bất cứ thứ gì bên trong.
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION