在我的类代码防止不变性吗?



我写过类,别人告诉我它们不是不可变的,但应该是不可变的。

public class Author {
private String name;
private String publisher;
public Author(String name, String publisher) {
this.name = name;
this.publisher = publisher;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getPublisher() { return publisher; }
public void setPublisher(String publisher) { this.publisher = publisher; }
}

和我的第二节课

public final class Book {
private final String title;
private final Author author;
private final Date datePublished;
public Book(String title, Author author, Date datePublished) { 
this.title = title;
this.author = author;
this.datePublished = datePublished;
}
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public Author getAuthor() { return author; }
public Date getDatePublished() { return datePublished; }
}

我相信设置方法防止不变性,但我错过了什么?get方法也能破坏不变性吗?如果是这样,这是否意味着。标题等等,setter也能阻止它吗?

Book类不是不可变的,因为这个方法:

public void setTitle(String title) { this.title = title; }

它也不会编译,因为不允许赋值。您不能在方法中分配给final字段。

Author类也不是不可变的。它有设置和可变(不是final)字段,并且不是final类。


get方法也可以破坏不变性吗?

嗯…这取决于他们做什么。例如,返回对(私有)数组字段的引用的getter使调用者有可能改变对象的(有效)状态。还有一个"getter";可以更新缓存或访问计数器,这可以视为一个突变。

在你的情况下,没有。(IMO)


getAuthor()不破坏不变性吗?既然Author是公开的,我不明白为什么现在不能。

实际上,可变性在Java中是一个相当复杂的属性,你可以得到不同的"答案"。

例如,你问Author是可变的这一事实是否也会使Book也是可变的。

  • 如果从建模的角度来看,答案可能是否定的。纠正作者名字的拼写并不会改变他们写的书。Author不是Book部分。(可能).

  • 但是如果你从(比如说)序列化Book的角度来看它,并且Author包含在序列化中,那么改变Author就改变了序列化的形式。

在这种情况下,我倾向于说它不会使Book可变。但在其他情况下,答案可能是不同的。

'不变性'意味着很多东西,它是一个定义模糊的术语。通常它的意思是"它的状态不能被直接改变"。

getAuthor()不改变任何状态-它只是让你观察它。是的,它是公共的,但那不是'不可变'的意思。不可变的,如'不能被改变',如'不变'。

java中的字符串是不变的。鉴于:

String x = "Hello";
someMethod(x);

你不可能在someMethod中写入任何可能导致x成为其他任何东西的内容。例如,x.toLowerCase()不会这样做:它不会改变x引用的对象-它会生成一个新的对象。

另一方面,给定:

Book b = new Book("Gulliver's Travels");
someMethod(b);
System.out.println(b.getTitle());

如果someemethod是:

可以打印'Hitchhiker's guide'
public void someMethod(Book b) {
b.setTitle("Hitchhiker's guide");
}

这就是为什么你的书是可变的。

当state在别处时,可变性变得有点模糊。例如,java.io.File只有final字段,没有setter,也没有方法改变它的任何内部状态(它拥有的字段)。然而,你仍然可以"修改它的状态,然后观察这个修改":someFileObj.delete();确实改变了一些东西,而且这种变化是相当可观察的。j.i file是否是不可变的取决于你问谁(它取决于你如何定义不可变)。您也可以使用静态identityhashmap进行一些疯狂的恶作剧,但让我们不要做得太过分-在大多数情况下,不可变性可以归结为非常非常简单的东西:

  • 将所有字段设置为final.

瞧。就是这样。你会发现根本不可能写出那个set方法。set方法从根本上与不变性的符号不兼容—充其量,您可以使用生成新的修改克隆的方法,就像"Hello".toLowerCase()不是setter一样—它通过克隆这个对象生成一个新的字符串对象,但是将其中的每个字符小写。按照惯例,这些方法被称为'with'方法:

public class Book {
private final String title, author;
public Book(String title, String author) {
this.title = title;
this.author = author;
}
public String getTitle() { return title; }
public String getAuthor() { return author; }
public Book withAuthor(String newAuthor) {
return new Book(this.title, newAuthor);
}
}

这个方法(withAuthor)不会改变你的书。它制作了一本新书,书名与这本书相同,但注明了作者。

注意:另一种常见的方式是,如果你有一个final字段是可变类型的。想象一下:

public class Book {
// books can actually have multiple authors...
private List<String> authors = new ArrayList<String>();
public List<String> getAuthors() { return authors; }
}

类是不可变的吗?在大多数情况下,并不是不可变的:

book.getAuthors().add("Reinier Zwitserloot");

的修复将是确保该列表也是不可变的。List.of是不可变列表,或者你可以通过包装它使它有效地不可变:list = Collections.unmodifiableList(someArrayList);将完成这项工作。

对于java的初学者来说,这是相当复杂的材料。让我们坚持一下:注意,如果字段类型可以包含可变的东西,那么就没有什么可做的了——没有setter和final字段不再有帮助。字符串,以及所有的原语——尽管都是不可变的。幸运的是。

相关内容

最新更新