为什么不可变的类不能使用setter方法?

时间:2019-06-11 10:19:57

标签: java immutability setter

我正在阅读有关不可变类的知识,据说使类不可变的方法是:

  • 1-将类定型为防止继承
  • 2-使可变变量成为最终变量
  • 3-不提供setter方法。

我认为第三个条件是不必要的。当我们将变量定为final并为其提供任何值时,即使通过setter方法也无法为其分配新值(因为一旦将值赋给final变量就无法更改) 。那么为什么我们需要没有设置方法的第三个条件呢?

我理解错误的方式吗?

5 个答案:

答案 0 :(得分:0)

您是对的。根据定义,设置器将使用给定值替换字段。如果所有字段都是最终字段,那么您无论如何都无法提供设置器。

我对如何编写不可变类的描述是:

  • 使所有个字段为最终字段
  • 确保每个字段的类型本身都是不可变的

如果您非常注意确保字段不变,则可以编写一个可变字段的不可变类,但是在这种情况下,您需要非常小心。

答案 1 :(得分:0)

是的,它可以减少为

  1. 将类定型以防止继承;
  2. 将可变变量定为最终变量,因此不必费心提供任何设置器;

但是出于教育目的,较短的项目符号可能会更好地工作-即使有些多余。

答案 2 :(得分:0)

public class Person{
  private String name;
  public Person(String name){
    this.name = name;
  }
  public String getName(){
    return this.name;
  }
  public void setName(String name) {
    this.name = name;
  }
}

现在,很明显,Person不是不可变类。这并不意味着Person的实例不能是(据说)不可变的另一个类的成员。

public final class MyImmutableClass {
  // p is final, so it can't be re-referenced
  private final Person p;

  public MyImmutableClass(Person p) {
    this.p = p;
  }
  // it can be altered, though
  public void setPersonName(String name) {
    this.p.setName(name);
  }
  public String toString() {
    return "Person: " + p.getName();
  }
}

现在,我们有一个不可变的类,但是它确实包含一个setter。该设置器会主动更改(最终)字段p的成员。

public static void main(String[] args) {
  MyImmutableClass c = new MyImmutableClass(new Person("OriginalName"));
  System.out.println(c);
  c.setPersonName("AlteredName");
  System.out.println(c);
}

然后..那里有。通过设置器更改成员(即使变量是最终的)。一定要理解,“最终变量”不一定是常数,在大多数情况下,它的状态可以更改。最终变量的意义在于它不能被重新引用。注意,我们也可以有这样一种方法:

public void setPerson(Person p) {
  this.p.setName(p.getName());
}

如果类型本身是不可变类型,或者如果它是原始类型,则最终变量本身仅是常量,但是您应该了解那里的大多数类型都是可变的。 类型是不可变的还是原始的,并且被声明为final?当然,添加一个二传手。但是,目的是什么?误导使用您班级的人?

答案 3 :(得分:0)

项目2中提到的变量可以是引用,即使变量本身为final,也可以是可变的(如列表或集合)。

这就是为什么我们拥有Collections.unmodifiableList之类的实用程序来使可变类几乎不变的原因。

因此,对setter的禁止是为了防止意外更改最终变量的状态。

答案 4 :(得分:0)

将字段最终定为不足以保证不变性。您需要在不可变的类中制作可变对象的防御性副本。

class Foo {
   private String str;
   public Foo(String str) {
      this.str = str;
   }

   public String getString() {
       return str;
   }
}

上面的类是不可变的,因为:

  • 字符串是不可变的。
  • “ str”字段是私有字段,不能更改。

现在考虑使用以下MyDate类。


public class MyDate {
    private Date; // date is not immutable
    public  MyDate(Date date) {
        this.date = date;
    }

    public Date getDate() {
        return date;
    }
}

上面的MyDate类不是一成不变的,因为用户可以执行以下操作:

Date d = new Date(<someDate>);
MyDate md = new MyDate(d);
d.set(<someDate>);  // oops, just changed value in MyDate via external reference.

也可以通过getDate()完成。

要使MyClass不可变,请在构造函数和getter中制作Date的防御性副本。这些会阻止类的用户更改date字段:

  • 使用对构造函数参数的引用。
  • 通过吸气剂检索日期字段
public class MyDate {
    private Date; // date is not immutable
    public  MyDate(Date date) {
        this.date = new Date(date);
    }

    public Date getDate() {
        return new Date(date);
    }
}
相关问题