因此,我们创建了一个带有一些私有类成员的简单类,并为它自动生成了getter。但是getter实际上返回了对该成员的引用,从而获得了对私有成员的完全访问权限。这样可以吗? 这是一个类的代码:
public class User {
private ArrayList<String> strings = new ArrayList(){ {
add("String1");
add("String2");
} };
public User() {
}
public ArrayList<String> getStrings() {
return strings;
}
public void setStrings(ArrayList<String> strings) {
this.strings = strings;
}
}
主要方法代码:
public class Main {
public static void main(String[] args){
User user = new User();
System.out.println(user.getStrings());
user.getStrings().add("String3");
System.out.println(user.getStrings());
}
}
输出:
[String1,String2]
[String1,String2,String3]
我把吸气剂换成了这个:
public ArrayList<String> getStrings() {
return (ArrayList<String>)strings.clone();
}
但问题仍然存在,即使不是为了安全,吸气剂是什么?什么是写它们的正确方法?
答案 0 :(得分:2)
不,它不可行,因为它打破了封装,因此类不能维护自己的不变量。与构造函数相同。
但问题不在于getter / setter,而在于自动生成它们的代码。
简而言之:不要盲目使用自动生成的访问者,如果他们处理可变结构,则制作防御性副本(或不可变的等价物)。
顺便说一句,我不会有一个ArrayList
返回类型的getter,即使它只是一个副本。它通常不是客户的业务,你返回的是什么样的列表,所以我的getter看起来像这样:
public List<String> getStrings() {
return new ArrayList<>(strings);
}
或使用不可变视图:
public List<String> getStrings() {
return Collections.unmodifiableList(strings);
}
或者使用Guava的ImmutableList
课程:
public List<String> getStrings() {
return ImmutableList.copyOf(strings);
}
三种解决方案之间存在细微差别,因此最佳解决方案可能会有所不同。作为一般规则,我更喜欢返回不可变结构,因为这清楚地表明对结构所做的更改不会被反映出来,即user.getStrings().add( "X" );
将因异常而失败。
您向我们展示的代码的另一个微妙问题是双括号初始化。想象一下这样的课程:
public class Foo {
private List<String> strings = new ArrayList() {{ add("bar");}};
private Object veryLargeField; //the object stored here consumes a lot of memory
public List<String> getStrings() {
return strings;
}
}
现在想象一下我们这样做:
private class Bar {
private List<String> fooStrings;
public Bar() {
this.fooStrings = new Foo().getStrings();
}
}
Bar
消耗多少内存(或使用精确术语:保留)?好吧,结果很多,因为你用双括号初始化做的是创建一个匿名内部类,它将包含对其外部类(Foo
)的引用,因此返回的列表是可访问的Foo
的所有其他字段都不符合垃圾回收的条件。
答案 1 :(得分:0)
从我的观点来看,吸气剂通常应该有两个目的:
如果您的示例违反了这些原则,则取决于具体情况: