1. 什麼是 getter 與 setter
如果把物件想像成保險箱,私有欄位就是保險箱的內容,而 getter 與 setter 就是通往各個小隔間的鑰匙。getter 讓你知道裡面放了什麼,setter 則能小心地把新東西放進去(前提是你不要把刺蝟放進去取代文件)。
Getter
Getter 是一個公開方法(public),會回傳私有欄位(private)的值。它的名稱通常以 get 開頭,後接欄位名的首字母大寫形式。
public class Person {
private String name; // 私有欄位
// name 欄位的 getter
public String getName() {
return name;
}
}
Setter
Setter 是一個公開方法(public),用來修改私有欄位的值。其名稱以 set 開頭,後接欄位名的首字母大寫形式。
public class Person {
private String name;
// name 欄位的 setter
public void setName(String name) {
this.name = name;
}
}
對於 boolean 欄位
對於類型為 boolean 的欄位,慣例是在 getter 中使用前綴 is:
private boolean active;
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
2. 語法與命名慣例
Java 是嚴謹但不死板的語言。已有一套行之有年的慣例,使你的程式碼對其他開發者(以及一個月後的你自己)來說更容易理解。
- Getter: public Type getFieldName()
- Setter: public void setFieldName(Type value)
- boolean 的 getter: public boolean isFieldName()
欄位名稱在方法名稱中以大寫字母開頭:如果欄位是 age,方法就會是 getAge() 與 setAge()。
這個慣例符合 JavaBeans 風格,因此 IDE、函式庫與框架可以自動找到你的 getter 與 setter。比如說,如果你使用 Spring 或 JavaFX,這些方法會在需要時被「像魔法一樣」自動呼叫。
3. 程式碼範例
讓我們拿一個教學專案——簡單的「通訊錄」應用(類似電話簿)——來加入正確的 getter 與 setter。
範例:具有私有欄位與 getter/setter 的 Contact 類別
public class Contact {
private String name;
private String phone;
private int age;
private boolean favorite;
// Getters
public String getName() {
return name;
}
public String getPhone() {
return phone;
}
public int getAge() {
return age;
}
public boolean isFavorite() {
return favorite;
}
// Setters
public void setName(String name) {
this.name = name;
}
public void setPhone(String phone) {
this.phone = phone;
}
public void setAge(int age) {
// 驗證範例:年齡不能為負數
if (age < 0) {
System.out.println("年齡不能為負數!");
return;
}
this.age = age;
}
public void setFavorite(boolean favorite) {
this.favorite = favorite;
}
}
在應用中的使用
Contact friend = new Contact();
friend.setName("Ivan Ivanov");
friend.setPhone("+1-999-123-45-67");
friend.setAge(25);
friend.setFavorite(true);
System.out.println("姓名:" + friend.getName());
System.out.println("電話:" + friend.getPhone());
System.out.println("年齡:" + friend.getAge());
System.out.println("最愛:" + (friend.isFavorite() ? "是" : "否"));
Setter 中的驗證範例
請注意,在 setAge 的 setter 中我們加入了簡單的檢查:如果年齡為負數,我們就不會修改欄位,並輸出警告。這是保護物件免於不正確資料的簡單方式。
4. Best practices:正確的做法
不是所有欄位都需要 public setter
有時欄位應該是唯讀——例如唯一識別碼,它在建立物件時指定,之後就不再更動。這種情況就不寫 setter:
public class Contact {
private final int id; // final——初始化後就不能再變更
public Contact(int id) {
this.id = id;
}
public int getId() {
return id;
}
// 沒有 setId!
}
使用 getter/setter 來控管存取與驗證
public void setName(String name) {
if (name == null || name.trim().isEmpty()) {
System.out.println("姓名不可為空!");
return;
}
this.name = name;
}
不要直接暴露內部的可變物件
如果你有一個欄位——例如電話號碼陣列:
private String[] phones;
不要在 getter 中直接回傳它:
public String[] getPhones() {
return phones; // 不佳!
}
這樣的程式碼允許外部程式碼任意修改該清單——破壞了封裝!
較正確的作法:回傳陣列的副本:
public String[] getPhones() {
return Arrays.copyOf(phones, phones.length); // 回傳副本
}
或直接進行 clone:
public String[] getPhones() {
return phones.clone();
}
讓 getter 與 setter 明確且簡單
- 不要在 getter/setter 中寫複雜的商業邏輯——它們的任務很單純:存取控制,必要時做驗證。
- 如果欄位不應變更——就不要提供 setter。
- 如果欄位不應對外公開——就不要提供 getter。
5. 在 IDE 中自動產生 getter/setter
手動撰寫存取子實在不怎麼愉快,尤其當一個類別有十來個欄位時。幸好,現代的 IDE(例如 IntelliJ IDEA、Eclipse、以及安裝外掛的 VS Code)都能自動產生它們。
在 IntelliJ IDEA 中
- 打開類別,將游標放在類別本體內。
- 按下 Alt + Insert(或 Code -> Generate...)。
- 選擇 Getter and Setter。
- 勾選需要的欄位並按下 OK。
搞定! 你的 getter 與 setter 會自動產生,彷彿變魔術一般。
在 Eclipse 中
- 打開類別。
- 滑鼠右鍵 — Source — Generate Getters and Setters...
- 選擇欄位並按下 OK。
在 VS Code(搭配 Java Extension Pack)中
- 打開類別檔案。
- 在指令面板(Ctrl+Shift+P)中輸入 Generate getters and setters。
- 依照指示操作。
6. 擴充你的應用:封裝的實際運用
在前面的講座中,你建立了一個用於儲存聯絡人的簡單應用。現在我們可以改進它,將欄位設為私有,並僅透過 getter/setter 提供存取。
之前(不佳範例):
public class Contact {
public String name;
public String phone;
public int age;
}
問題:任何程式碼都可以這麼做:
Contact c = new Contact();
c.age = -1000; // 現在我們的電話簿裡有吸血鬼了!
之後(良好範例):
public class Contact {
private String name;
private String phone;
private int age;
public void setAge(int age) {
if (age < 0) {
System.out.println("年齡不能為負數!");
return;
}
this.age = age;
}
public int getAge() {
return age;
}
// 其他 getter/setter...
}
現在就不可能在外部不小心(或故意)把物件搞壞。
7. 針對可計算與不可直接修改的屬性的 getter/setter
有時候值不存放在欄位中,而是即時計算:
public class Rectangle {
private int width;
private int height;
public int getArea() {
return width * height;
}
}
面積不需要 setter——它無法被直接設定,只能改變寬或高。
8. Getter 與 Setter:常見錯誤
錯誤 №1:getter/setter 破壞封裝。
如果 getter 回傳內部可變物件的參照(例如清單),外部程式碼可能會直接修改它,繞過所有檢查。這會削弱封裝的目的。
錯誤 №2:setter 不做資料驗證。
如果 setter 只是指派值而不檢查,物件可能會進入不正確的狀態(例如負的年齡或空白姓名)。
錯誤 №3:為所有欄位自動產生 setter。
IDE 可以為所有欄位產生 setter,但這不一定總是正確!例如對識別碼(id)就不需要 setter。
錯誤 №4:在 getter/setter 中放入複雜邏輯。
getter 與 setter 應保持簡單。如果其中出現複雜的商業邏輯,應將其抽離到獨立方法中。
錯誤 №5:違反命名慣例。
如果把 getter 命名為 fetchName() 而不是 getName(),某些框架與函式庫可能無法辨識它。
GO TO FULL VERSION