Java 将对象传递给方法并更改属性

Java 将对象传递给方法并更改属性,java,parameter-passing,Java,Parameter Passing,如果我有一个如下所示的类: class A { private List<B> bs = new ArrayList<B>; public List<B> getBs() { return bs; } } 当然,这样可以修改原始列表中的对象,这就是我想要的。 然而,我发现类C修改类A的listbs中对象的值是非常令人困惑的。对于另一个开发人员来说,很难看到这种方法改变值。 所以我认为我在这里所做的被认为是坏习惯,不是吗` 编写此类

如果我有一个如下所示的类:

class A {
   private List<B> bs = new ArrayList<B>;

   public List<B> getBs() {
    return bs;
   }
}
当然,这样可以修改原始列表中的对象,这就是我想要的。 然而,我发现类C修改类A的list
bs
中对象的值是非常令人困惑的。对于另一个开发人员来说,很难看到这种方法改变值。 所以我认为我在这里所做的被认为是坏习惯,不是吗`
编写此类代码的更好方法是什么?

我同意你的推理。此方法:

public void setB(B b) {
    this.b = b;
    this.b.ChangeValue();
}
class A {
    private List<B> bs = new ArrayList<B>;

    public List<B> getBs() {
        return bs;
    }
}
没有很好地实现。最好对其进行重构。如果这不是一个选项,它应该清楚地记录
B
参数在传递给方法时被修改。如果可能的话,也应该从方法名中明确这一点。也许你可以把它命名为
setAndUpdate(B)


此方法:

public void setB(B b) {
    this.b = b;
    this.b.ChangeValue();
}
class A {
    private List<B> bs = new ArrayList<B>;

    public List<B> getBs() {
        return bs;
    }
}
我认为我在这里所做的被认为是不好的做法,不是吗

是的,通过getter公开类的可变部分可能会产生误导,因为数据封装得不够好。更改数据没有什么错,只是以一种不会立即被视为修改的方式进行

没有通用的规则来解决这个问题。通常,应该避免直接返回类中的可变对象。相反,在适当的情况下提供getter和setter,并且不要将
List
提供给调用者:

class A {
     private List<B> bs = new ArrayList<B>;

     public void getSizeB() {
         return bs.size();
     }
     public B getB(int i) {
         return bs.get(i);
     }
     public void setB(int i, B b) {
         bs.set(i, b);
     }
     public void addB(B b) {
         bs.add(B);
     }
     ... // Add more methods as needed
}
A类{
私有列表bs=新的ArrayList;
public void getSizeB(){
返回bs.size();
}
公共B getB(国际一级){
返回bs.get(i);
}
公共空间设置(int i,B){
b.集合(i,b);
}
公共无效地址B(B){
bs.添加(B);
}
…//根据需要添加更多方法
}

这样做的目的是控制进入
列表的内容:因为没有直接访问,所以可以验证进入列表的每个
B
。使用类
A
的代码必须明确说明它所做的所有修改,因为它无法直接获取列表并对其执行任何操作。

首先,我认为
getBs()
是个坏主意,因为该方法的调用方可以清除列表或向其中添加元素,对于
a
的状态,这将是一个比仅仅更改列表中某个元素的属性更严重的更改

您可以将其替换为
getB(int)
,它返回该列表的第i个元素。 现在,如果
B
是不可变的,那么您的工作就完成了,
getB(int)
的调用者在A的状态下不能更改任何内容

如果B是可变的,您可以决定
getB(int)
将返回
bs.get(i)
的副本

总而言之,我将替换:

public void getBs() {
    return bs;
}
两者都有

public void getB(int i) {
    return bs.get(i);
}


当然,你也应该添加范围检查,以确保
i
是一个有效的索引。

看看这篇文章,公开支持列表并不一定是件坏事。许多标准集合允许您使用例如keySet或entrySet获取支持数据结构的可修改视图。如果您的不变量(如内部大小字段)必须与列表同步,则情况就不同了。@aioobe如果您有HashMap之类的数据结构,即使不使用keySet或entrySet,也可以直接更改它。作为HashMap的所有者,您可以使用它做任何您想做的事情。但是一旦你把HashMap放到你的类中,你就要小心如何允许你的类的用户操作HashMap。谢谢,问题是列表中的对象应该是可变的。这就是我想要的。然而,我的问题是如何更清楚地说明这个对象实际上是被修改的。因此,如果你有一个
房子
,你应该公开一些方法,比如
关闭窗口(windowId)
?我认为最好使用
house.getWindow(windowId.close()
。如果
房子
需要保持一个不变量,比如
numberOfOpenWindows
,那当然是另一回事了,但在我看来,暴露你的部分状态并不一定是坏事。@aioobe我的回答建议
a.getB(bId).doSomething()
-相当于你的
房子.getWindow(windowId).close()
。我反对
house.getWindows().get(windowId).close()
,而不是
house.closeWindow(windowId)
。这当然取决于
A
类的目的和责任。如果客户端能够修改其包含的
B
s的集合,我认为最好提供一个访问
B
s和文档集合的方法,该方法公开了一个支持数据结构,而不是复制几个列表方法,并简单地将它们委托给
bs
列表。我想通过
house.getWindow
展示的是,公开并不总是一个坏主意作为您状态一部分的可变对象。@aioobe我不介意公开可变对象甚至集合,但通常我通过其只读包装器公开集合,以保持对其中内容的控制。
public void getB(int i) {
    return bs.get(i);
}
public void getB(int i) {
    return new B(bs.get(i));
}