是否遵循java约定只为集合提供getter?
我最近遇到了如下代码:是否遵循java约定只为集合提供getter?,java,Java,我最近遇到了如下代码: public List<Item> getItems() { if (items == null) { items = new ArrayList<Item>(); } return this.items; } 而不是 foo.setItems(myArrayList) 我以前没有见过这个习惯用法,我不能说我喜欢它,但是当我使用mapstruct.org(顺便说一下,这是一个很棒的工具)生成一些映射代码时
public List<Item> getItems() {
if (items == null) {
items = new ArrayList<Item>();
}
return this.items;
}
而不是
foo.setItems(myArrayList)
我以前没有见过这个习惯用法,我不能说我喜欢它,但是当我使用mapstruct.org(顺便说一下,这是一个很棒的工具)生成一些映射代码时,mapstruct处理得很好,并且正确地生成了使用getter作为setter的代码
我只是想知道,这是不是一个常见的习语,我不知怎么错过了?对我来说,这似乎毫无意义,但也许这背后有一些我没有看到的智慧?您的代码中有两件事:
ArrayList
的引用,这不好,因为它会中断。如果要将项目添加到此列表,则应仅公开addItem()
方法add()
时,它们可以删除对象。他们可以从不同的线程修改它,使您的代码以有趣和令人沮丧的方式中断。他们甚至可以将它转换为一个原始的列表
,并用完全不同类型的对象填充它,使您的代码抛出ClassCastException
s
换句话说,它使强制类不变量变得不可能。
请注意,有两种因素共同导致此问题:
public String getFoo() {
return this.foo;
}
因为字符串
是不可变的。这也很好:
public List<String> getFooList() {
return new ArrayList<>( this.fooList );
}
public List get傻瓜列表(){
返回新的ArrayList(this.傻瓜列表);
}
因为现在您返回的是防御副本,而不是实际对象。(但是,如果列表中的元素是可变的,您将再次遇到麻烦。)
这个问题有一个更微妙的变化。。。 想象一下这个场景:
public class Foo {
private List<String> list;
public Foo( List<String> list ) {
this.list = list; // Don't do this
}
...
}
公共类Foo{
私人名单;
公共食物办事处(名单){
this.list=list;//不要这样做
}
...
}
这看起来完全无害,你可以在很多地方看到。然而,这里也有一个隐藏的陷阱:在存储列表之前不复制,你就处于完全相同的情况。你无法阻止某人这样做:
List<String> list = new ArrayList<>();
list.add( "nice item" );
Foo foo = new Foo( list );
list.add( "hahahaha" );
list.add( "i've just added more items to your list and you don't know about it." );
list.add( "i'm an evil genius" );
List List=new ArrayList();
列表。添加(“佳品”);
Foo Foo=新Foo(列表);
列表。添加(“哈哈哈”);
添加(“我刚刚在你的列表中添加了更多的项目,而你对此一无所知。”);
添加(“我是一个邪恶的天才”);
因此,在将可变对象分配给字段之前和返回字段时,都应该制作防御性副本。
如果公开一个类的可变字段是如此危险,那么为什么人们不一直制作防御副本呢? 除了不知道为什么这不是一个好主意外,还有两大类借口
这是一个常见的习惯用法,但我不建议使用它,因为调用者可以执行以下操作:
final List<Item> items = bean.getItems();
final List notItems = (List) items;
notItems.add(new NotAnItem());
及
公共作废设置项(最终列表项){
if(items==null){
抛出新的NullPointerException(“items为null”);
}
this.items=新阵列列表(items);
}
我认为这令人困惑。当我只有getter时,我希望返回不可修改的集合。我最好将它命名为getItemList()
,以表明可以在其上放置元素。实际上,在我看来,这比从外部接受任何未知列表要好。至少,该类可以保证其列表是可变的,并且是ArrayList。更好的办法是避免暴露列表,并提供addItem()方法。setItems()
也会让人困惑。“设置”是指“如果为空则设置或替换现有”addItems()
会更清楚。@JBNizet它更好,但只是在关闭但不锁上前门比敞开大门更安全的意义上。@biziclop同意,因此我的评论的第二部分:不要公开列表,通过专用的addItem()方法对其进行修改。谢谢。我不清楚这对懒惰有什么帮助。毕竟,如果目标只是不情愿地生成对象,那么使用这种方法,您生成的新对象不发出任何信号,而不是返回null?只需将该方法设置为returnitems,就可以生成较少的对象?如果我有一个预先存在的arraylist,我想用它来填充这个结构,我必须将所有元素复制到新创建的arraylist中,而不是仅仅调用setItems——同样,生成更多的对象?@user4782738返回null会
final List<Item> items = bean.getItems();
final List notItems = (List) items;
notItems.add(new NotAnItem());
public List<Item> getItems() {
if (items == null) {
return Collections.emptyList();
}
return Collections.unmodifiableList(this.items);
}
public void setItems(final List<Item> items) {
if (items == null) {
throw new NullPointerException("items is null");
}
this.items = new ArrayList<>(items);
}