我遇到过一个具有不可变属性的类:
MyObject[] allObjs
该属性初始化如下:
List<MyObject> objs = createAllMyObjects();
allObjs = objs.toArray(new MyObject[objs.size()]);
当它通过访问者公开时,它以列表形式完成:
public List<MyObject> getAllMyObjects() {
return Collections.unmodifiableList(Arrays.asList(allObjs));
}
为什么程序员会这样做?有什么好处我不知道吗?
性能不是一个问题,因为objs数组只会在几十个元素中出现。
似乎我们正在四处转圈。
这个类是一种工厂,所以它有一个私有的构造函数,只暴露静态方法(不确定这是否是疯狂的原因)。
修改
我想我的问题确实是,“为什么不使用内部List<MyObject> allObjs
代替MyObject[] allObjs
,并返回Collections.unmodifiableList(allObjs)?”因为这会完成同样的事情。
答案 0 :(得分:6)
我看到的唯一原因是“在误用的情况下提前失败”(如果这种设计选择不是随机的并且有意义,那么他的信任很少就是他的特定客户)。
MyObject[]
具体化,在运行时检查,而List<MyObject>
则不然。可以在后者中隐藏非MyObject引用(当然,他会在编译时得到未经检查的警告),这将在ClassCastException
的某个未定义的未来点失败,但他不能对{{1}做同样的事情。 - 在滥用时会立即失败。
请注意,如果客户端是在生成之前使用此API的预构建二进制文件,那么没有人会收到未经检查的警告,因此如果此代码尝试迁移前泛型客户端使用的代码,则这是有意义的。
(实现相同结果的另一种方法是使用MyObject[]
并且还需要List<T>
,以便后者可以提供运行时类型检查。但这需要例如另一个构造函数参数 - 现有的客户端不会自动利用它。**更新:**这基本上描述了Sauer在第一条评论中提到的API调用,因此不需要重新发明这个,只需使用那个:))。
答案 1 :(得分:2)
可能有几个原因:
List
更改已修补,无需进行批量更改。如果代码的其余部分使用该数组,则可能没有人想要进行所有其他更改。for(:)
一样迭代,但是可以通过[]
代替get()
。就个人而言,使用阵列的理由还不够,但也许只是我。List
仍然是引用数据的手段,任何不注意其余(希望注释的)代码的人都可能会尝试添加到{{1}这将导致运行时UnsupportedOperationException。使它成为一个数组使它的用法清晰。答案 2 :(得分:1)
似乎作者想要返回一个不可变的副本并且已经冗余地完成了。我假设选择了List作为返回类型,因此可以使用MyObject类型轻松创建克隆。
答案 3 :(得分:1)
有充分的理由不返回对内部MyObject[]
属性的引用。通过引用,调用者可以对数组进行更改。
如果没有理由为什么结果必须是List
,您还可以返回数组的副本:
public MyObject[] getAllMyObjects() {
MyObject[] result = new MyObject[allObjs.length];
system.ArrayCopy(allObjs, 0, result, 0, result.length);
return result;
}
在添加问题后,更新: 你是正确的,如果将内部属性更改为List,则将其包装为不可修改更容易。
类使用数组而不是List的原因可能是性能。如果数组在对象的生命周期内没有发生太多变化并且经过多次迭代,那么数组的开销就比List少。
如果性能是使用数组的原因,你可以通过存储List来防止在getter中创建额外的对象,仅用于返回,即:
allObjsList = Collections.unmodifiableList(createAllMyObjects());
allObjs = allObjsList.toArray(new MyObject[allObjsList.size()]);
像以前一样使用allObjs数组,并在getter中返回存储的列表:
public List<MyObject> getAllMyObjects() {
return allObjsList ;
}
答案 4 :(得分:1)
让我回答一个笑话:
这位新犹太新娘正在为她的丈夫做第一次大餐,并尝试用她母亲的牛腩配方,以她母亲的方式切断烤肉。 Hubby认为肉是美味的,但是,“你为什么要切断两端 - 这是最好的部分!”她回答说,“这就是我母亲总是这样做的。
下周他们去了老布比的房子,她准备了着名的胸肉配方,再次切断了两端。年轻的新娘确信她一定会遗漏一些重要的信息,所以她问奶奶为什么要切断两端。奶奶说,“Dahlink,这是它适合平底锅的唯一方式!”
虽然我们当然可以找到一些论证来证明某一特定决定的合理性,但在实践中,这很少会产生任何影响,并且通常没有这样的决定背后的意图。
使用最简单易读的选项。可读性通常比聪明更重要。
答案 5 :(得分:0)
我认为使用数组而不是列表没有任何优势。列表更舒适。也许那个人喜欢使用数组:)
答案 6 :(得分:0)
我认为这背后的原因可能是数组不可修改。但是,您可以将内部列表保留为List,并在getter中返回它的副本,这样内部列表仍然无法从外部修改。
或使用Collections.unmodifiableList()返回的“不可修改”列表......
在这种情况下,我也没有看到任何性能优势,因为无论如何来回转换需要一些周期。
答案 7 :(得分:0)
这种方式集合是不可修改的,即调用者不能修改成员数组。 无法使用数组执行此操作,因此编码器会将其复制到列表中。
答案 8 :(得分:0)
在Java Generics书中,作者实际上说,由于我们有泛型,我们应该专门使用集合,不应该再使用数组。我个人现在倾向于远离数组,因为集合帮助我记住了使用一系列事物的意图,例如。将vs vs list作为对阵数组的对象,它并没有真正告诉我有关数据的任何信息。
除了可能是快速复制(System.arraycopy())
之外,对于我不能用数组做的集合没有任何我无法做的事情。