Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/cmake/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么允许设置集合是不好的做法?_Java - Fatal编程技术网

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

Java 为什么允许设置集合是不好的做法?,java,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;

假设我们有一个具有简单集合(例如列表)的类。该类包含构造函数、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;
    }
}
类示例{
私有字符串id;
私人名单名称;
公共示例(字符串id、列表名称){
this.id=id;
this.names=名称;
}
公共字符串getId(){
返回id;
}
公共列表getNames(){
返回姓名;
}
公共无效集合id(字符串id){
this.id=id;
}
公共无效集合名(列表名){
this.names=名称;
}
}

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

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

这将为您提供两种更改内容的方法(
getNames()。添加(…)
setNames(Arrays.asList(…)
)。
这令人困惑


您应该选择一个选项,并使另一个选项不可用。

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

编辑


需要明确的是,防御措施是我所说的验证和确认的一部分。

许多内置集合是可变的,因此存储这样的值可能会允许外部类修改示例的内部状态。考虑下面的片段:

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"]!
List name=new ArrayList();
名称。添加(“堆栈”);
名称。添加(“溢出”);
示例=新示例();
例如:集合名(名称);
//示例.getNames()返回[“堆栈”,“溢出”]
名称。添加(“交易所”);
//getNames现在返回[“堆栈”、“溢出”、“交换”]!
更安全的方法是复制已通过列表的内容:

public void setNames(List<String> names){
   this.names = new ArrayList<>(names);
}
public void集合名(列表名){
this.names=新的ArrayList(名称);
}

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

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

引用有效的Java第二版第15项:“最小化可变性”:

[使类不可变]:不可变类有很多很好的理由 比可变类更易于设计、实现和使用。他们不那么容易上瘾 会出错,并且更安全

中还描述了不可变类


(*)这并不是说你不应该使用它们;只是您应该将类设计为不可变的默认位置,并且只在实际需要的少数情况下使它们可变——这比您想象的要少。要在有效Java中引用同一项,请执行以下操作:

类应该是不可变的,除非有很好的理由使它们成为不可变的 易变的


同样的原因,如果没有防御性副本,您不应该从getter返回集合。它破坏了封装。构造函数和setter的问题是一样的:因为你没有采取防御副本,调用方可以更改
示例
持有的列表。get和set方法首先暴露对象的内部状态,这违反了最重要的OO原则信息隐藏/封装。如果此类用于没有业务逻辑的数据传输对象,则这可能是正常的。在任何其他情况下,都应该避免使用get和set方法。您可以在这些方法中进行防御性编程,只传递值,而不传递对对象的引用。OU在这方面有一些很好的材料,“你可以防御性地编程”这是一厢情愿的想法。getter和setter打破了信息隐藏/封装支持特性envive,这使得代码更难理解、更难重用和维护。(同样:信息隐藏/封装不适用于DTO,因此getter和setter可以使用它们。)那么您建议如何初始化对象并更新它们包含的值?如果没有它们,您如何从对象获取信息?正如我已经提到的:DTO有getter/setter。如果您希望/需要从业务对象(包含业务逻辑)获取信息,则存在设计问题。