Common Getter Setter Mistakes In Java

2021/3/19

Getter/Setter

在 Java 中,Getter/Setter 是最常用的两个用来获取和更新一个 Class 内部状态的方法。 有了 Getter/Setter 就可以在一定程度上限制调用方。 比如:

public void setNumber(int num) {
    if (num < 10 || num > 100) {
        throw new IllegalArgumentException();
    }
    this.number = num;
}

但是,如果无脑写了 Getter/Setter 就会带来很多隐藏问题。

给 Public 的变量写 Getter/Setter

比如:

public String firstName;

public void setFirstname(String fname) {
    this.firstName = fname;
}

public String getFirstName() {
    return this.firstName;
}

这两个方法毫无意义

在 Setter 中直接传递引用

如果在 Setter 中传递一个引用类型,那会带来隐藏的副作用。

private int[] scores;

public void setScore(int[] src) {
    this.scores = src;
}

这里的问题是,无论 scores 还是外部传递进来的 src 被修改了,都会影响双方。 因为引用指向的是同一份数据。

如何解决这种引用传递的问题? 就是在赋值的时候复制一份内容。

public void setStore(int[] src) {
       this.scores = new int[src.length]
       System.arraycopy(src, 0, this.score, 0, src.length);
   }

在 Getter 中直接返回内部对象的

private int[] scores;

   public int[] getScores() {
       return this.scores;
   }

这么直接返回的的问题,就和前面在 Setter 中直接赋值一样,内外相互影响。 因为这么返回/赋值,实际上你改变了这个 scores 实例的 accessibility。 解决方法也一样,复制一份再返回。

public int[] getScores() {
       int[] copy = new int[this.scores.length];
       System.arraycopy(this.scores, 0, copy, 0, copy.length);
       return copy;
   }

给原始类型和 String 提供 Getter 和 Setter

原始类型(int, float, double, boolean, char) 和 String 类型,可以放心的实现 getter 和 setter,

Make defensive copies when needed

这句话是 Joshua Bloch 在他著名的 Effective Java 中提到的。

private Date birthDate;

   public Date getBirthDate() {
       return (Date)this.birthDate.clone();
   }

clone() 方法返回一个 Object 对象,因此需要做类型转换。

private List<Person> persons;

   public List<Person> getPersons() [
       return (List<Person>)this.persons.clone();
   ]

这里即使返回了一个 clone 的list,但是 list 里包含的对象,还是指向原来的 person instance, 为何? 因为 List/ArrayList 的 clone() 方法是一个浅 copy, shallow copy。

怎么办, 自己实现 full copy。

总结

getter setter 写起来简单,但是如果你不思考,写出了 bug 的话,通常会很难发现

Comments