7

Can I make an Object immutable when passed as a parameter, so that the called method can't change it but the callee can?

So I have somthing like this:

Class Car {
  Wheel wheel_1;

  Axis axis = new Axis(wheel_1);
}


Class Wheel {
  int size;

  setSize(int size) {}
  int getSize() {}      
}

Now I construct a car with a wheel. Then from class car I want to construct an axis.

For that I pass wheel_1 to the constructor of Axis.

Now my question: Can I asure somehow that the constructor of Axis doesnt change the size of wheel_1 but class car can change it.

0

6 Answers 6

7

Yes. Typically this is done by utilising a copy-constructor for the Wheel class.

For example:

wheel_1 = new Wheel(wheel);

Bear in mind, the Wheel class will need to be written in a way that supports this. That is, it should either offer a copy-constructor.

Note: this hasn't made the object immutable. It has merely produced a copy that can't be edited by anything outside your class.

If you return the Wheel instance from any of your other methods, be sure to defensively copy it on the way out too:

Wheel getWheel() {
  return new Wheel(wheel_1);
}

Finally, it's worth mentioning that it's always a good idea to create immutable classes whenever you can. So perhaps you can avoid this issue by actually making Wheel immutable?

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you for your answer :). I also thought of that. Just wanted to know if there is a way or "keyword" e.g. final (its not final), that could be used.
In C#, ICloneable is not recommended as the caller cannot distinguish whether the clone is a deep or shallow copy. Is Cloneable the same in java and thus should not be used?
@chrisw69 Yes, clone() is generally not loved by the Java community either, see here, for example. I've edited the post to remove any mention of it - probably not worth encouraging it at all.
@DuncanJones I have doubts if this is a good answer to the question. Please note (or correct me if I'm wrong) but in your example if Car changes the size of it's wheel, the wheel referenced by Axis will not be modified. They are separate entities. I think the OP's intention was for Car to be able to modify Axis's Wheel by modifying his own. I.e. They were supposed to be the same object, but Axis shouldn't have "write access rights".
4

Make the Wheel class immutable. Then let the Car object create a new Wheel object when it needs a new size.

public final class Wheel {
    private final int size;

    public Wheel(int size) {
        this.size = size;
    }

    public int getSize() {
        return size;
    }
}

Now you can pass the wheel object to the Axis without any problems.

public class Car {
    private Wheel wheel;
    private Axis axis;

    public Car(int initialWheelSize) {
        wheel = new Wheel(initialWheelSize);
        axis = new Axis(wheel);
    }
}

Comments

3

Pass the Axis constructor a copy of wheel_1.

class Car {
    Wheel wheel_1;

    Axis axis = new Axis(new Wheel(wheel_1));
}

Also, Wheel will need a constructor which takes another Wheel object and copies its properties.

Comments

3

I would have Wheel implement an interface that has only accessor methods, i.e: -

Example

interface WheelInterface
{
  int getSize();
}

class Wheel implements WheelInterface
{
  // methods
}

class Axis
{
  public Axis(WheelInterface wheel)
  {
    // Only getSize will be available
  }
}

Now, simply pass WheelInterface instead of Wheel and only the accessor methods will be available to the constructor of your Axis class

Benefits

The benefits of doing this is that there is no copying required; you are simply providing a contract to the Axis class and that contract states that it can only get the size, not change it.

By passing the same object reference by value, you aren't having to call any copy constructor and don't have to worry about deep and shallow copying semantics. I would not want to use Clone on my wheel, I think that feels a little dirty for reasons mentioned in comments of other answers.

In an object-oriented pattern, using an interface to abstract away what you don't need is also a sign of good design. If your wheel has snow tyres on, your Axis class probably doesn't even need to care!

Drawbacks

As others have mentioned, it is possible to cast the interface back to a concrete type (as others have mentioned, assuming you know what you're casting to); this is called downcasting and I don't recommend it; if this were done in a similar scenario, it probably wouldn't get past a code review.

Comments

2

Why don't you send the parameter as its superclass which doesn't have setSize() method

Class Car {
  Superwheel wheel_1;

  Axis axis = new Axis(wheel_1);
}

class Superwheel
{
   int size;
 int getSize() {}    
}

Class Wheel extends Superwheel{
  int size;

  setSize(int size) {}
  int getSize() {}      
}

6 Comments

One can still typecast Superwheel to Wheel and change the size.
Hm I like this approach! because there would still be only one object. But Subir is right one could still change it. In my usecase it is a good solution because I just didnt want to add Wheel = new Wheel(wheel) everytime.
Seems like there's some duplication with the super class, why not use an interface?
Intefaces can also be casted.
Yes you are right, interfaces can be downcast but is usually not recommended, however there is code duplication involved in the base class example shown above. Furthermore, I wouldn't want to create a base class for the purpose of making an object immutable as inheritance is more of an 'is a' relationship, not a 'behaves like' relationship.
|
1

Yes, you can. If you make Car and Wheel in the same package and declare Wheel setter fields to be protected. So, Axis if declared in another package can't access setters of Wheel, thus making Wheel immutable from Axis point of view.

Well, this solution is basic to start with. Ofcourse, you can think of whether to make Wheel final or not, cloning, serializable, etc. based on what level you want to make immutable.

2 Comments

Well this is not really a good approach in my opinion. Changing the package structure just to achieve this goal. But a working answer. Thank you!
@eclipse Thanks, it depends on the requirement. If you are working on a simple program, you may not want to make it complex. Like I said, it is a basic approach and depending how far you go, you can use other suitable techniques.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.