为什么允许设置集合是不好的做法?

时间:2017-09-05 19:51:39

标签: java

假设我们有一个带有简单集合的类(例如列表)。该类包含构造函数,getter和setter。 我被告知直接设置集合是一种不好的做法。

class Example{
    private String id;
    private List<String> names;

    public Example(String id, List<String> names){
        this.id = id;
        this.names = names;
    }

    public String getId(){
        return id;
    }

    public List<String> getNames(){
        return names;
    }

    public void setId(String id){
        this.id = id;
    }

    public void setNames(List<String> names){
       this.names = names;
    }
}

有人能指出编写方法setNames()的缺点吗?

6 个答案:

答案 0 :(得分:4)

set和get操作背后的逻辑是允许验证或替换内部表示,如果你让外部类设置特定的实现,你就失去了对插入逻辑的控制(允许重复?有序?,是可变的吗?) ,并且你让对象更难使用,因为它的用户必须决定,当他们很可能不关心时。

答案 1 :(得分:2)

由于私有变量名由您的类拥有,因此您可以确保您可以控制其在类中的内容。如果您将该变量的引用更改为传入的列表,那么您不再确定您的实例不会被外部更改,因为您的类和传递新列表实例的类都将具有引用/访问它。 getNames()也是如此 - 任何调用该方法的类现在都具有从类外部更改列表内容的完全访问权限。

答案 2 :(得分:2)

这会为您提供两种更改内容的方式(getNames().add(...)setNames(Arrays.asList(...))) 这很令人困惑。

您应该选择一个选项并使其他选项无法使用。

答案 3 :(得分:2)

很多内置集合都是可变的,因此存储这样的值可能允许外部类修改Example的内部状态是一种你没有计划的方式。请考虑以下代码段:

List<Stirng> names = new ArrayList<>();
names.add("Stack");
names.add("Overflow");

Example example = new Example();
example.setNames(names); 
// example.getNames() returns ["Stack", "Overflow"]

names.add("Exchange");
// example.getNames now returns ["Stack", "Overflow", "Exchange"]!

更安全的方法可能是复制传递列表的内容:

public void setNames(List<String> names){
   this.names = new ArrayList<>(names);
}

答案 4 :(得分:1)

这里的答案略有不同, setter是一种不好的做法(*),无论你是设置集合属性还是其他类型。

引用 Effective Java 2nd Ed 第15项:&#34;尽量减少可变性&#34;:

  

[使类不可变]有很多好的理由:不可变类   比可变类更容易设计,实现和使用。它们不太容易发生   错误,更安全。

the Oracle tutorial中还有对不可变类的描述。

(*)这并不是说你永远不应该使用它们;只是你应该将类设计为不可变的默认位置,并且只在实际需要的几个场合使它们变得可变 - 而且这种情况比你想象的要少。在Effective Java中引用相同的项目:

  

除非有充分的理由制作它们,否则类应该是不可变的   可变

答案 5 :(得分:-2)

使用get和set方法可以在输入引发问题之前对输入执行验证和验证。它还可以用于转换进出系统的信息,以方便程序和用户。

修改

为了清楚起见,防御性措施是我所谈论的验证和验证的一部分。