简体   繁体   中英

Getter for private member object in Java

Say I have a class called Truck and one of the private member variables is of class Wheel . A getter for the Wheel variable, getWheel , would return a reference to it, as follows:

class Truck{
    private Wheel wheel;

    Truck(){
        wheel=new Wheel();

    }

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

class Wheel{
    int color;
}

Now, whoever calls getWheel will be able to modify the private member object at will:

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

        v.color=2;
    }
}

This would defeat encapsulation, wouldn't it?

What would be the right remedy for this?

The usual approaches are:

  • Make a defensive copy (eg, return a copy of Wheel )
  • Make Wheel immutable (any time you want to change it, you create a new one instead, constructed with the new color)
  • Don't return Wheel , return an immutable interface on Wheel that only exposes getters, no mutation operations
  • As Sandeep said , make the setter more restricted than the getter , say, package-private for the setter and public for the getter. Then classes within the package could set the color of Wheel , but classes outside the package cannot. (I prefer #3 in this situation for the clear separation, but this works too if you're crossing a visibility boundary.)

That third option is one of the reasons why making instance variables (fields) non- private is often considered poor practice.

Here's #3 in more depth, just because it's more complicated than #1 and #2, not because it's necessarily better (it isn't, design choices are made in context).

A read-only interface, typically public or package-private depending on how you're going to use all of this stuff:

public interface Wheel {
    int getColor();
}

The concrete class, typically package-private (could be a private static nested class within Truck if that's the only place it's used):

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 , typically same visibility as Wheel :

public class Truck {
    private WheelImplementation wheel;

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

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

Sure, that can be defeated through reflection, but generally you design an API for use, rather than for abuse. :-)

You can make a private/package setter for wheel color. Also, initialize the color in the constructor which would help you do that.

And like other answers, returning a copy of Wheel is also a good way to do this.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM