我的 class 中的代码是否会阻止不变性?
Does code in my class prevent immutability?
我写了 classes,我被告知它们不是不可变的,但应该是。
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; }
}
还有我的第二个 class。
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; }
}
我相信 set 方法可以防止不可变性,但我是否遗漏了其他东西? get 方法也能破坏不变性吗?如果是这样,是否意味着 this.title 等 setter 也可以阻止它?
Book
class 不是不可变的,因为这个方法:
public void setTitle(String title) { this.title = title; }
它也不会编译,因为不允许赋值。您不能分配给方法中的 final
字段。
Author
class 也不是一成不变的。它具有设置器和可变(不是 final
)字段,并且不是 final
class.
Can the get methods break immutability too?
嗯...这取决于他们做什么。例如,返回对(私有)数组字段的引用的 getter 使调用者可以改变对象的(有效)状态。而“getter”可以更新缓存或访问计数器,这可以被视为突变。
但在你的情况下,不。 (IMO)
Doesn't the getAuthor()
break immutability? I can't understand why it wouldn't now since Author
is public.
实际上,可变性在 Java 中是一个相当复杂的 属性,根据不可变的实际含义,您可以得到不同的“答案”。
例如,您询问 Author
是可变的这一事实是否也会使 Book
也可变。
如果从建模的角度来看,答案很可能是否定的。更正作者姓名的拼写不会改变他们所写的书。 Author
不是 Book
的 部分。 (大概)。
但是如果你从(比如说)序列化一个Book
的角度来看,并且Author
包含在序列化中,那么改变Author
改变序列化形式。
在这种情况下,我倾向于说它不会使 Book
可变。但在其他情况下,答案可能会有所不同。
'immutability' 有多种含义,这是一个模糊定义的术语。最常见的意思是 'its state cannot be directly changed'.
getAuthor()
不会改变任何状态 - 它只是让您观察它。是的,它是 public,但这不是 'immutable' 的意思。不可变的,如 'not capable of being mutated',如 'unchanging'.
java 中的字符串不变。鉴于:
String x = "Hello";
someMethod(x);
您在 someMethod
中没有任何内容可以使 x
成为其他任何内容。例如,x.toLowerCase()
不会这样做:这不会更改 x 所指的对象 - 这会生成一个 new 对象。
另一方面,给定:
Book b = new Book("Gulliver's Travels");
someMethod(b);
System.out.println(b.getTitle());
如果 someMethod 是:
,则可以打印“Hitchhiker's guide”
public void someMethod(Book b) {
b.setTitle("Hitchhiker's guide");
}
这就是为什么你的书是可变的。
当状态在别处时,可变性变得有点模糊。例如,java.io.File
只有 final 字段,没有 setters,并且没有方法改变它的任何内部状态(它拥有的字段)。但是,您仍然可以 'modify its state and then observe this modification': someFileObj.delete();
确实改变了一些东西,而且这种改变是显而易见的。 j.i.File 是否不可变取决于你问谁(这取决于你如何定义不可变)。您还可以使用静态 IdentityHashMaps 进行一些疯狂的恶作剧,但不要太过分——在大多数情况下,不变性归结为非常非常简单的事情:
- 使所有字段
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
) 不会更改您的图书。它制作了一本新书,与本书同名,但注明作者。
注意:另一种常见的进入模糊术语的方法是,如果你有一个可变类型的最终字段。想象一下:
public class Book {
// books can actually have multiple authors...
private List<String> authors = new ArrayList<String>();
public List<String> getAuthors() { return authors; }
}
这个 class 是不可变的吗?对于大多数意图和目的来说,它不是一成不变的:
book.getAuthors().add("Reinier Zwitserloot");
解决方法是确保该列表也是不可变的。 List.of
制作不可变列表,或者您可以通过包装它使其有效地不可变:list = Collections.unmodifiableList(someArrayList);
将完成工作。
这对于第一步 java 来说相当复杂 material。让我们坚持:请注意,如果字段类型可以包含可变内容,那么您无能为力 - 没有 setter 并且拥有 final 字段不再有帮助。字符串和所有原语——不过都是不可变的。还好。
我写了 classes,我被告知它们不是不可变的,但应该是。
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; }
}
还有我的第二个 class。
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; }
}
我相信 set 方法可以防止不可变性,但我是否遗漏了其他东西? get 方法也能破坏不变性吗?如果是这样,是否意味着 this.title 等 setter 也可以阻止它?
Book
class 不是不可变的,因为这个方法:
public void setTitle(String title) { this.title = title; }
它也不会编译,因为不允许赋值。您不能分配给方法中的 final
字段。
Author
class 也不是一成不变的。它具有设置器和可变(不是 final
)字段,并且不是 final
class.
Can the get methods break immutability too?
嗯...这取决于他们做什么。例如,返回对(私有)数组字段的引用的 getter 使调用者可以改变对象的(有效)状态。而“getter”可以更新缓存或访问计数器,这可以被视为突变。
但在你的情况下,不。 (IMO)
Doesn't the
getAuthor()
break immutability? I can't understand why it wouldn't now sinceAuthor
is public.
实际上,可变性在 Java 中是一个相当复杂的 属性,根据不可变的实际含义,您可以得到不同的“答案”。
例如,您询问 Author
是可变的这一事实是否也会使 Book
也可变。
如果从建模的角度来看,答案很可能是否定的。更正作者姓名的拼写不会改变他们所写的书。
Author
不是Book
的 部分。 (大概)。但是如果你从(比如说)序列化一个
Book
的角度来看,并且Author
包含在序列化中,那么改变Author
改变序列化形式。
在这种情况下,我倾向于说它不会使 Book
可变。但在其他情况下,答案可能会有所不同。
'immutability' 有多种含义,这是一个模糊定义的术语。最常见的意思是 'its state cannot be directly changed'.
getAuthor()
不会改变任何状态 - 它只是让您观察它。是的,它是 public,但这不是 'immutable' 的意思。不可变的,如 'not capable of being mutated',如 'unchanging'.
java 中的字符串不变。鉴于:
String x = "Hello";
someMethod(x);
您在 someMethod
中没有任何内容可以使 x
成为其他任何内容。例如,x.toLowerCase()
不会这样做:这不会更改 x 所指的对象 - 这会生成一个 new 对象。
另一方面,给定:
Book b = new Book("Gulliver's Travels");
someMethod(b);
System.out.println(b.getTitle());
如果 someMethod 是:
,则可以打印“Hitchhiker's guide”public void someMethod(Book b) {
b.setTitle("Hitchhiker's guide");
}
这就是为什么你的书是可变的。
当状态在别处时,可变性变得有点模糊。例如,java.io.File
只有 final 字段,没有 setters,并且没有方法改变它的任何内部状态(它拥有的字段)。但是,您仍然可以 'modify its state and then observe this modification': someFileObj.delete();
确实改变了一些东西,而且这种改变是显而易见的。 j.i.File 是否不可变取决于你问谁(这取决于你如何定义不可变)。您还可以使用静态 IdentityHashMaps 进行一些疯狂的恶作剧,但不要太过分——在大多数情况下,不变性归结为非常非常简单的事情:
- 使所有字段
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
) 不会更改您的图书。它制作了一本新书,与本书同名,但注明作者。
注意:另一种常见的进入模糊术语的方法是,如果你有一个可变类型的最终字段。想象一下:
public class Book {
// books can actually have multiple authors...
private List<String> authors = new ArrayList<String>();
public List<String> getAuthors() { return authors; }
}
这个 class 是不可变的吗?对于大多数意图和目的来说,它不是一成不变的:
book.getAuthors().add("Reinier Zwitserloot");
解决方法是确保该列表也是不可变的。 List.of
制作不可变列表,或者您可以通过包装它使其有效地不可变:list = Collections.unmodifiableList(someArrayList);
将完成工作。
这对于第一步 java 来说相当复杂 material。让我们坚持:请注意,如果字段类型可以包含可变内容,那么您无能为力 - 没有 setter 并且拥有 final 字段不再有帮助。字符串和所有原语——不过都是不可变的。还好。