CodeGym /課程 /JAVA 25 SELF /Getter 與 Setter:語法與最佳實務

Getter 與 Setter:語法與最佳實務

JAVA 25 SELF
等級 15 , 課堂 2
開放

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 中

  1. 打開類別,將游標放在類別本體內。
  2. 按下 Alt + Insert(或 Code -> Generate...)。
  3. 選擇 Getter and Setter
  4. 勾選需要的欄位並按下 OK。

搞定! 你的 getter 與 setter 會自動產生,彷彿變魔術一般。

在 Eclipse 中

  1. 打開類別。
  2. 滑鼠右鍵 — SourceGenerate Getters and Setters...
  3. 選擇欄位並按下 OK。

在 VS Code(搭配 Java Extension Pack)中

  1. 打開類別檔案。
  2. 在指令面板(Ctrl+Shift+P)中輸入 Generate getters and setters
  3. 依照指示操作。

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(),某些框架與函式庫可能無法辨識它。

留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION