Java中获取私有成员变量的Getter方法

5

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

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;
    }
}

这会破坏封装性,不是吗?对此应该怎么办才好?

1
返回 wheel 的副本。 - Robby Cornelissen
我认为这并不违反封装,你误解了封装的含义,它并不意味着私有对象不能被修改,你的代码是我们通常所做的正常行为。 - Spark.Bao
2个回答

8
通常的做法有以下几种:
  • 制作防御性副本(例如,返回Wheel副本
  • 使Wheel不可变(每次要更改它时,创建一个新的Wheel,用新颜色构造它)
  • 不要返回Wheel,在Wheel上返回一个只公开getter方法而没有变更操作的不可变接口
  • 正如Sandeep所说,使settergetter更受限制,例如,将setter设为包私有,getter设为public。然后,在包内的类可以设置Wheel的颜色,但是包外的类则不能。(在这种情况下,我喜欢选#3,因为它更清晰明了,但是如果你要跨越可见性边界,则这也很有效。)

第三种方法是为什么将实例变量(字段)设置为非private通常被认为是不好的做法之一。

以下是更详细的第三种方法,仅因为它比#1和#2更复杂,而不是因为它一定更好(它并不是,设计选择是基于特定的情况)。

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

public interface Wheel {
    int getColor();
}

具体类通常为包私有(如果只在Truck内使用,可以是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;
    }
}

卡车,通常与轮子的能见度相同:

public class Truck {
    private WheelImplementation wheel;

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

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

当然,通过反射可以打败它,但通常你是为使用而设计API,而不是为滥用而设计。 :-)

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

2
你可以为轮子颜色设置一个私有/包装器。 此外,初始化颜色的构造函数也会帮助你实现这一点。
与其他答案类似,返回Wheel的副本也是解决方案之一。

不错的观点,在某些情况下这可以起作用,特别是当您希望防范的API消费者位于可见性边界之外时(如package-private vs. public)。 - T.J. Crowder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接