3

I'm passing a variable from main to a method in another class. I thought that the variable could be changed inside the method, without being changed in main. Using loads of print statements, I've found that my 'updatePosition' method actually modifies the variable 'newVelocity' and the modification somehow finds its way back to the main class. How do I stop this?? Here's the loop in the main class where the method is called:

//do{

        projectile.updatePosition(newPosition, newVelocity, timeStep, g);   
        System.out.println("This is newVelocity");
        newVelocity.print();     //This variable has been modified!!! Doesn't print what it should
        projectile.getNewPosition();
        System.out.println("This is new position");
        newPosition.print();


        newPositionForNewtonsLaw = newPositionForNewtonsLaw.add(distance,newPosition);
        newG = earth.aDueToGravity(earthMass, earthRadius, newPositionForNewtonsLaw);

        projectile.updateVelocity(newVelocity, timeStep, g, newG);
        newV = projectile.getNewVelocity();
        System.out.println("new velocity");
        newV.print();
        g=earth.aDueToGravity(earthMass, earthRadius, newPositionForNewtonsLaw);
        System.out.println("This is newG");
        newG.print();
        g.print();
        newVelocity = projectile.getNewVelocity();
        positionX=newPosition.getX();
        positionY=newPosition.getY();
        System.out.println(positionX);
        System.out.println(positionY+"y");
    //}while (positionY>0);

And here's the method that's modifying the variable, even though I'm sure it shouldn't!

public PhysicsVector updatePosition(PhysicsVector initialPosition, PhysicsVector initialVelocity, double timeStep, PhysicsVector a){

    PhysicsVector v = new PhysicsVector();  
    v=initialVelocity;
    a.scale(0.5*timeStep);
    v.increaseBy(a);
    v.scale(timeStep);
    System.out.println("This is initialVelocity");
    initialVelocity.print();

    initialPosition.increaseBy(v);
    return initialPosition;
}

newVelocity is an object (from a class to make vector) and I don't know if it can be declared public, static, private etc, or whether any of those things would actually help.

user13948
  • 443
  • 1
  • 6
  • 14
  • this is how objects work. Pass a copy to it. Additionaly mark it as `final` – Royal Bg Nov 26 '15 at 16:51
  • Java is pass-by-value. That means if you pass a variable x with value object O, then after the call x will still have as value object O. However fields of O might be changed. – Joop Eggen Nov 26 '15 at 16:52
  • Its FAR easier to step through your code with a debugger rather than littering your program with print statements which you then have to go back and remove – user1231232141214124 Nov 26 '15 at 16:55
  • @RoyalBg How do I pass a copy? Sorry, really new to java. – user13948 Nov 26 '15 at 16:56
  • Create a new object and fill it with the values of the old one. Then pass the new one. Additionally you can make your object immutable. DO NOT provide methods that changes its state, instead the methods should return new object. This is how e.g. the `String` class works – Royal Bg Nov 26 '15 at 16:57
  • this topic can be interesting: http://stackoverflow.com/questions/869033/how-do-i-copy-an-object-in-java – guillaume girod-vitouchkina Nov 26 '15 at 17:35

3 Answers3

2

Java is pass-by-value. If you pass a PhysicsVector object to your updatePosition method you are passing a copy of the reference which actually points to the same object on the heap.

You should pass a copy of the object to updatePosition or create a copy in that method.

To create a copy you could introduce a copy constructor to your PhysicsVector class

public PhysicsVector(PhysicsVector originalVector){
    // Setup the values with those contained in originalVector
}

Then you would instantiate a new PhysicsVector as follows in your updatePosition() method

PhysicsVector v = new PhysicsVector(initialVelocity);  

Alternatively you could create a method to copy that does the same thing and returns a PhysicsVector

kstandell
  • 775
  • 6
  • 16
1

This is the real problem of using mutable objects. When you are playing with the mutable concept, you should be really aware of it.

One of the solutions is to create a new object and fill it with the internals of the old one and pass it.

Another could be TO NOT modify the object inside the method.

The third one is to stop using mutable objects and declare your classes to be immutable. Any method that makes a change, instead returns a new object with the new state

PhysicsVector increaseBy(PhysicsVector another) {
    PhysicsVector newOne = new PhysicsVector(another.X + this.X);
    return newOne;
}

instead of

void increaseBy(PhysicsVector another) {
    this.X += another.X;
}
Royal Bg
  • 6,988
  • 1
  • 18
  • 24
0

if you want the method to not be able to change the values of its arguments :

public PhysicsVector updatePosition(final PhysicsVector initialPosition, 
final PhysicsVector initialVelocity, final double timeStep, final PhysicsVector a){....}

this will prevent the method from changing the value of its arguments

Rafik BELDI
  • 4,140
  • 4
  • 24
  • 38
  • So is it normal for a method to change values passed to it?? New to java, as you can probably tell! – user13948 Nov 26 '15 at 16:51
  • yes it is it can changes the value, I thought that you don't wanna change the values but it seems that you want it to be changed am i correct ? – Rafik BELDI Nov 26 '15 at 16:53
  • No, you're right, I don't want to change it! Just wondered why it isn't automatically not changed. I thought java was 'pass by value', which I understood to mean methods can't change their arguments. – user13948 Nov 26 '15 at 16:54
  • It does not change the value of the variable, but its internals. – Royal Bg Nov 26 '15 at 16:55
  • @RoyalBg I've just googled 'internals java' and found nothing that makes sense. What are the internals? Are they the arguments I used to create the PhysicsVector object? – user13948 Nov 26 '15 at 18:16
  • english? do you speak it? – Royal Bg Nov 26 '15 at 21:44
  • 1
    @RoyalBg I do, yes, so your sarcasm is much appreciated. I meant, I found nothing that makes sense to ME, given that the information was all very technical. Or at least, for someone who's been learning java for four weeks. But thanks anyway, I found something that explains it now, and it was very helpful in understanding my problem. – user13948 Nov 28 '15 at 14:46
  • You can link it here so the others can be enlightened too. Btw. I really meant "internals" by "internals", in the terms of the objects internals, e.g. it's fields – Royal Bg Nov 28 '15 at 16:41
  • Setting final in front of the PhysicsVector argument does not prevent the method from modifying the state of the object, but only prevents it from reassigning it. It is therefore confusing and misleading. A much better approach is to add a unit test that verifies that the state of the input argument has not changed after the method is called, if that is indeed the requirement. Another thing you can do is to name the method to indicate whether or not the state change is expected. If the method changes the state, consider returning void instead. – 4thex Dec 04 '18 at 13:55