【问题标题】:Getter for private member object in JavaJava中私有成员对象的getter
【发布时间】:2017-03-31 07:14:56
【问题描述】:

假设我有一个名为Truck 的类,其中一个私有成员变量属于Wheel 类。 Wheel 变量 getWheel 的 getter 将返回对它的引用,如下所示:

class Truck{
    private Wheel wheel;

    Truck(){
        wheel=new Wheel();

    }

    Wheel getWheel(){
        return this.wheel;
    }
}

class Wheel{
    int color;
}

现在,任何调用getWheel的人都可以随意修改私有成员对象:

class me{
    public static void main(String[] args){
        Truck ye=new Truck();
        Wheel v=ye.getWheel();

        v.color=2;
    }
}

这会破坏封装,不是吗?

什么是正确的补救措施?

【问题讨论】:

  • 返回wheel的副本。
  • 我不认为它破坏了封装,你误解了封装,这并不意味着私有对象不能被修改,你的代码是我们通常做的正常行为。

标签: java oop


【解决方案1】:

通常的做法是:

  • 制作防御性副本(例如,返回Wheel副本
  • 使Wheel 不可变(只要您想更改它,就创建一个新的,用新颜色构建)
  • 不要返回 Wheel,在 Wheel 上返回一个不可变接口,它只公开 getter,没有变异操作
  • As Sandeep said,使 settergetter 更受限制,例如,setter 的 package-private 和 getter 的 public。那么包内的类可以设置Wheel的颜色,而包外的类不能。 (在这种情况下,我更喜欢 #3 以实现清晰的分隔,但如果您正在跨越可见性边界,这也适用。)

第三个选项是为什么将实例变量(字段)设为非private 通常被认为是不好的做法的原因之一。

这里的 #3 更深入,只是因为它比 #1 和 #2 更复杂,而不是因为它一定更好(事实并非如此,设计选择是根据上下文做出的)。

只读接口,通常是公共的或包私有的,具体取决于您将如何使用所有这些东西:

public interface Wheel {
    int getColor();
}

具体类,通常是包私有(如果这是唯一使用它的地方,则可以是 Truck 内的私有静态嵌套类):

class WheelImplementation implements Wheel {
    private int color;

    WheelImplementation(int color) {
        this.color = color;
    }

    public int getColor() {
        return this.color;
    }

    void setColor(int color) {
        this.color = color;
    }
}

Truck,通常与Wheel 相同的可见性:

public class Truck {
    private WheelImplementation wheel;

    Truck(){
        this.wheel = new WheelImplementation(/*...initial color...*/);
    }

    Wheel getWheel() {
        return this.wheel;
    }
}

当然,这可以通过反射来解决,但通常您设计一个 API 是为了使用,而不是为了滥用。 :-)

【讨论】:

  • 退后一步...所以通过将wheel 标记为私有成员,我所做的只是将wheel 对象的指针设为私有。任何能够获得地址/指针的人都能够修改对象,就像我在上面所做的那样。我的理解正确吗?
  • @FreshAir:是的,从技术上讲。唯一的完整防御是上面的#1:你没有给他们参考。但是#2 和#3 通常已经足够好了:你给他们引用,但是由于他们拥有引用的类型(Wheel 而不是WheelImplementation,即使它指向WheelImplementation 对象),他们除非他们诉诸反射,否则无法修改实例。当您使用 API 时,如果您使用反射来修改您收到的内容的内部结构,那么您就是在滥用 API。 :-)
  • 感谢克劳德先生的详细回答;我还在消化你提到的高级方法。我注意到有人在我的问题下发表了评论,他/她似乎暗示了我的问题中的代码,对明显的问题没有任何补救措施,是行业通常如何做的事情 - 坦率地说,我对此有点惊讶。你同意这个评论吗?
  • @FreshAir:这完全取决于TruckWheel 的使用方式。如果重要的是TruckWheel 不被从getWheel 接收的代码修改,那么不,这不是通常的做法。如果Wheel 是否被从getWheel 获取的代码修改并不重要,那么是的,这就是通常的做法。
【解决方案2】:

您可以为车轮颜色制作私有/包设置器。 另外,在构造函数中初始化颜色,这将有助于你做到这一点。

和其他答案一样,返回 Wheel 的副本也是这样做的好方法。

【讨论】:

  • 好点子,这可以在您要防御的 API 使用者跨越可见性边界(例如包私有与公共)的某些情况下工作。
猜你喜欢
  • 2011-08-23
  • 1970-01-01
  • 2016-05-22
  • 2018-06-10
  • 2017-12-22
  • 2015-05-15
  • 2013-12-30
  • 2011-09-26
相关资源
最近更新 更多