我在网上遇到这个练习,我有两个课程,我应该让Tutor课程一成不变。但是,我唯一能想到的是在name字段中添加final。当进入构造函数时,我不认为我需要更改name变量的初始化,因为String是不可变的。我不确定如何处理集合以及如何使构造函数的这一部分不可变。 根据练习,我不应该改变学生班(我能看到的是可变的)
public class Student {
private String name;
private String course;
public Student(String name, String course){
this.name = name;
this.course = course;
}
public String getName(){
return name;
}
public String getCourse(){
return course;
}
public void setName(String name) {
this.name = name;
}
public void setCourse(String course){
this.course = course;
}
}
public final class Tutor {
private String name;
private final Set<Student> tutees;
public Tutor(String name, Student[] students){
this.name = name;
tutees = new HashSet<Student>();
for (int i = 0; i<students.length; i++)
tutees.add(students[i]);
}
public Set<Student> getTutees(){
return Collections.unmodifiableSet(tutees);
}
public String getName(){
return name;
}
}
答案 0 :(得分:3)
list2env
课程提出了促进其不变性的许多方面:
list2env(lst, envir = .GlobalEnv)
Data1
# SaleID Country
#1 Sale1 US
#2 Sale3 US
Data2
# SaleID Country
#1 Sale2 Mexico
#2 Sale6 Mexico
Data3
# SaleID Country
#1 Sale4 Canada
#2 Sale5 Canada
#3 Sale7 Canada
受到保护以免受修改然而,构造函数的防御性副本并不完整
它还必须复制传递的数组的Tutor
个元素。否则,构造函数的客户端可能会更改它们的任何实例,并使Set<Student>
实例变为可变,例如:
Student
你应该写一些类似的东西:
Tutor
另请注意,Student[] students = ...;
Tutor tutor = new Tutor(name, students);
students[0].setName("new Name!"); // break the immutability of Tutor
返回的public Tutor(String name, Student[] students){
this.name = name;
tutees = new HashSet<Student>();
for (Student student : students){
Student copy = new Student(student.getName(),
student.getCourse());
tutees.add(copy);
}
}
是不可修改的,但包含在Set
中的元素是可变的。
因此,为了使Tutor不可变,您还必须在返回getTutees()
时创建Student元素的副本,例如:
Student
正如您可能已经注意到的那样,在这些条件下获得不变性(我们希望不可变但包含引用可变实例的集合的实例)需要编写更多代码(读取/维护/测试)并执行更多处理(执行起来慢得多)
如果getTutees()
是一个不可变类,原始public Set<Student> getTutees(){
Set<Student> students = new HashSet<>();
for (Student student : tutees){
Student copy = new Student(student.getName(),
student.getCourse());
students.add(copy);
}
return Collections.unmodifiableSet(students);
}
和原始构造函数就足够了。
答案 1 :(得分:2)
正确的方法是使对象不可变为:
答案 2 :(得分:1)
你真的需要归还学生集吗?如果你真的需要它,你可以通过使用只提供getter的接口来隐藏它,比如
interface IStudent {
public String getName();
public String getCourse();
}
class Student : implements IStudent { ...}
并在导师中返回Set<IStudent>
答案 3 :(得分:0)
要使Tutor类不可变,您应该在Tutor中的所有字段上使用“final”修饰符,而不是在Tutor的类定义上。
答案 4 :(得分:0)
您可以使用 JEP 395: Records 特性(作为 Java SE 16 的一部分引入)来创建一个不可变的类,而无需太多仪式。
如果您已经浏览了上面的链接,那么您一定已经想出可以简单地做到这一点
record Tutor(String name, Set<Student> tutees) { }
反过来你得到的是:
final class Tutor
。Tutor(String name, Set<Student> tutees)
相同的规范构造函数。private final
字段、name
和 tutees
及其对应的具有相同名称和返回类型的 public
访问器方法。equals
、hashCode
和 toString
方法。Student.java
record Student(String name, String course) { }
Tutor.java
import java.util.Set;
record Tutor(String name, Set<Student> tutees) { }
Main.java
import java.util.Set;
class Main {
public static void main(String[] args) {
Set<Student> cscStudents = Set.of(
new Student("Harry", "Java-8"),
new Student("Tina", "Java-9"),
new Student("Andy", "Java-11")
);
Set<Student> scienceStudents = Set.of(
new Student("Tony", "Phy"),
new Student("Kerry", "Chem"),
new Student("John", "Bio")
);
Tutor t1 = new Tutor("Mark", cscStudents);
Tutor t2 = new Tutor("Robin", scienceStudents);
Tutor t3 = new Tutor("Mark", Set.of(
new Student("Andy", "Java-11"),
new Student("Harry", "Java-8"),
new Student("Tina", "Java-9")
)
);
System.out.println(t1);
System.out.println();
System.out.println(t1.tutees());
System.out.println();
System.out.println("Students of " + t1.name() + ":");
t1.tutees()
.stream()
.forEach( t -> System.out.println(t.name()) );
System.out.println();
System.out.println(t1.equals(t2));
System.out.println(t1.equals(t3));
}
}
输出:
Tutor[name=Mark, tutees=[Student[name=Andy, course=Java-11], Student[name=Harry, course=Java-8], Student[name=Tina, course=Java-9]]]
[Student[name=Andy, course=Java-11], Student[name=Harry, course=Java-8], Student[name=Tina, course=Java-9]]
Students of Mark:
Andy
Harry
Tina
false
true