duke_from_java_by_reallyn00b-d2zdiy7

Java: Keep your privates private!

Java access modifiers can be tricky because methods behave differently when they return a primitive vs. an object! If you don’t watch out, you’ll let your private variables out to the world!

It’s easier than you think to expose yourself. Consider the subtle difference between the following two programs, SeemsTotallyFineAndItIs, and SeemsTotallyFineButItsNot.

In the first program, we have a private member x, and a standard accessor method, getX().

SeemsTotallyFineAndItIs:

public class SeemsTotallyFineAndItIs {

    private int x = 0;

    public int getX() {
        return x;
    }

    public static void main(String[] args) {
        SeemsTotallyFineAndItIs demo = new SeemsTotallyFineAndItIs();
        System.out.println("We start with: " + demo.getX());
        int x = demo.getX();
        x = 10;
        System.out.println("\nWe end with: " + demo.getX());
    }

}

On lines 12 and 13, the client code creates its own local variable x with the value returned from getX(), then reassigns the variable to 10.

Obviously this has no effect on the value of the member variable x, and it outputs 0 as both the value we start with and end with:

We start with: 0

We end with: 0

…which is totally fine. There’s no way here for client code to change our private variable because its a primitive type, and the getX() method that exposes it, exposes the value and not the reference.

Now consider a subtle change from an int to an object:

public class SeemsTotallyFineButItsNot {

    private Point point = new Point();

    public Point getPoint() {
        return point;
    }

    public static void main(String[] args) {
        SeemsTotallyFineButItsNot demo = new SeemsTotallyFineButItsNot();
        System.out.println("We start with: " + demo.getPoint());
        Point p = demo.getPoint();
        p.x = 10;
        p.y = 10;
        System.out.println("\nWe end with: " + demo.getPoint());
    }

}

Unfortunately this outputs:

We start with: (0, 0)

We end with: (10, 10)

Now, changing the values of local variable p actually changes the values of the class member point, because p contains a reference, not a value!

Clearly, we don’t want just anyone to be able to change the value of a private variable, otherwise what’s the point? But unfortunately this is just the default behavior of Java code. You have three options for dealing with this.

Option 1. Don’t make an accessor for a private variable.

I would argue this should be goto option #1. Your most important, and the first, option that you reach for. Why does your class need a getPoint() (or getWhatever) method? If you can’t think of a reason, then don’t make one.

Option 2. Make a more specific method.

If you do know a reason or reasons, then make a method for each of those reasons. Let’s say the reason you wanted to provide a getPoint() method was because client code occasionally needs to move the point to the right, then make a public void moveRight(int howFar) method instead. This not only keeps your private variable private, but it also is a better design.

Option 3. Clone.

OK so you really, really, really want a getPoint() method. (I can’t imagine what for.) Then, fine! Do it like this:

public Point getPoint() {
    Point p = new Point(this.point.x, this.point.y);
}

This way it doesn’t matter what any client code does with the point. They have their copy, and you have yours!

Conclusion

In conclusion, You don’t want anyone to be able to reach into your class and futz with the values of private variables. Be careful what, and how, you expose! Keep your privates private!

Leave a Reply

Your email address will not be published. Required fields are marked *