tags:

views:

110

answers:

7

I'm having a hard time figuring out how to pass by reference in Java. I currently have a class as follows

class Example {
    ArrayList<Foo> list;
    Bar hello;

    Example(ArrayList<foo> list, Bar hello) {
        this.list = list;
        this.hello = hello;
    }
}

In my main class, I initialize an array list of Foo, and a Bar object. Then I initialize Example passing in the array list and Bar objects. What I want is whenever the objects I passed to Example are updated in the Main class, they get updated in Example too. But for now, only ArrayList is updated. But Bar hello isn't. What makes ArrayList different from Bar that it updates but Bar doesn't?

Edit

Ok here's part of the actual code:

public class Main {
    static int
            board_n = 1, // amount of boards
            rows = 8, // board width
            cols = 8; // board height
    static ArrayList<Board> boardlist;
    static Player player1 = new Player("Mr");
    static Player player2 = new Player("9000");
    static Player currentPlayer = player1;
    static Move dummy = null;

    public static void main(String[] args) {
        int random_row, random_col, random_board, rand_i;
        Dimension size = new Dimension(rows, cols);
        boardlist = new ArrayList(board_n);
        for(int i = 0; i < board_n; i++) {
            boardlist.add(new Board(i, size));
        }

        Rules rulez = new Rules(boardlist, dummy, currentPlayer);
        rulez.setPieces();

        Random generator = new Random();

        while(rulez.getPiecesAvailable("Checker", player1.getName()) > 0 || rulez.getPiecesAvailable("Checker", player2.getName()) > 0) {
                // initialize points and generate random location
                random_row = generator.nextInt(rows);
                random_col = generator.nextInt(cols);
                random_board = generator.nextInt(board_n);
                Point point = new Point(random_row, random_col);

                // initialize pieces and randomly set moveable
                Piece hello1 = new Checker(player1.getName(), Integer.toString(random_row), Integer.toString(random_col), Integer.toString(0), -1);
                Piece hello2 = new Checker(player2.getName(), Integer.toString(random_row), Integer.toString(random_col), Integer.toString(0), 1);

                // add piece to board
                if(rulez.validateAddition(new Location(point, random_board), hello1))
                    boardlist.get(random_board).addPiece(hello1, point);
                if(rulez.validateAddition(new Location(point, random_board), hello2))
                    boardlist.get(random_board).addPiece(hello2, point);
        }

        currentPlayer = player2;
    }
}

When I create a Rules object, I pass boardlist, dummy, and currentPlayer to it. When I add stuff to boardlist, the boardlist inside Rules is the same one as the one outside. But on the last statement, when I change currentPlayer to player2, it doesn't change in Rules.

+2  A: 

If Bar is not a primitive type, then any changes made to Bar outside of your class should also affect the Bar within your class.

If you are trying to change Bar by reassigning another object to the variable, then changes will not be reflected within your class. (I have a feeling this may be the case in your code)

However, if Bar is a primitive type (int, double, char, etc...), then you cannot make changes to it in the way you are doing it right now.

Solution!

In this line of code of yours:

currentPlayer = player2;

You expect changes to be reflected in you class. They will not. You should add a changeCurrentPlayer(Player p) method and reassign the current player within your class.

The implementation of the method may look something like:

public void changeCurrentPlayer(Player newPlayer){
    this.player = newPlayer;
}
jjnguy
A: 

Without seeing more of your code it's impossible to say for sure but I have a hunch that your problem is that you are reassigning hello to another instance after you passed it to Example's constructor. The reason it works as you expect for the ArrayList is because you're calling methods on the same ArrayList instance without ever reassigning the actual list, which is what was passed to Example's constructor.

EDIT:

Ok, now that I've seen the actual code you've posted, I see that my hunch above was correct. When you do this:

currentPlayer = player2;

you're assigning currentPlayer to a different instance. It no longer points to the same instance that you passed to the Rules constructor. What you really want to do is something more like this:

rulez.setPlayer(player2);

Of course your Rules class needs a setPlayer(Player player) method defined for this to work.

Asaph
+2  A: 

In Java there is no pass by reference, only pass by value. However, if you have a class on your argument list (such as ArrayList or Bar), you pass a reference to its instance by value, therefore any changes you made to them will be visible after the method call.

I suspect you just set a new reference to the parameter, something like this:

 public void doNotDoThis(Bar hello) {
     hello = new Bar();
     hello.setFoo("new value"); // this won't be visible
 }

On the other hand, this one works as you'd expect:

 public void thisIsOk(Bar hello) {
     hello.setFoo("new value");
 }
candiru
A: 

There is no difference between list and hello. If you call any method on an external object that you have passed to the Example constructor you actually call it on the class member as well. You have not posted your whole code so I can only guess that maybe what you have in mind is this:

bar hello = new bar("abc");
Example example = new Example(list, hello);
hello = new bar("xyz");

In that of course in the third line you're not "updating" the hello object, but changing it's reference to another one. If you have done something like below it would update the member field of the example:

bar hello = new bar("abc");
Example example = new Example(list, hello);
hello.setValue("xyz");
quosoo
I posted this before I saw your code, but that's exactly your case. You're changing the reference rather than updating an object by calling some method on it.
quosoo
A: 

currentPlayer in the main class is not the same object that is in your Rules class, they are seperate. The currentPlayer object will exist in memory as a seperate entity from the one in the Rules class, so you might want add a function in Rules to change your player to the one you want. Maybe something like this:

public void changePlayer(Player aPlayer)
{
    this.player = aPlayer;
}
shawnjan
+1  A: 

Java, like C, only has pass by value. There is no pass by reference. References are passed by value.

Take this class for example:

class X
{
    private int a;

    public X(final int val) { a = val; }
    public void setA(final int val) { a = val; }
    public int getA() { return (a); }
}

If we use it like this:

public class Y
{
    public static void main(final String[] argv)
    {
        X x = new X(42);

       foo(x);
       System.out.println(x.getA());
    }

    private static void foo(final X x)
    {
        x.setA(7);
    }
}

The output will be 7. This shows that Java either passes the reference "x" by reference or by the value of the reference.

If we use it like this:

public class Y
{
    public static void main(final String[] argv)
    {
        X x = new X(42);

       foo(x);
       System.out.println(x.getA());
    }

    private static void foo(X x)
    {
        x = new X(28);
        x.setA(7);
    }
}

You get 42 which shows that Java passes the reference by value.

So when an assignment is done somewhere it does not alter what the original (or in the case of your code, the copy) is pointing at. There is no way (well no good way) to do what you want in Java.

What you need to do is provide a setCurrenPlayer(final Player player) method in your Rules class.

Also don't be cool and call the variable "rulez", it is just "rules". It is very common to do things like Foo foo; or Bar bar; when naming variables. If the class has a good name then using it for the variable name is generally a good idea :-)

TofuBeer
Thanks this makes sense and it's too bad what I'm trying to do can't be done. :( And about calling it "rulez", this is only a temporary Main class I made to test the Rules class. I'm sure the one who initializes Rules, which will not be my class because I'm working in a group, will use a proper variable name.
this is a dead end
As I tell my son (and students) there is no point in practising the wrong stuff. If you start naming things poorly then you will likely continue doing it. In short never write what you think is throw away code... all you do is get good at writing throw away code :-)
TofuBeer
A: 

Variables actually are only references to objects so both player objects (player1 and player2) live in memory and the variable currentPlayer only points to the place in memory where the player1 object is located.
When you reassign a variable like in

currentPlayer = player2;

You are only changing where the variable points to.

When you pass a variable to a method like in

Rules rulez = new Rules(boardlist, dummy, currentPlayer);

only the value (and not a reference) of currentPlayer is passed. And the value is the location where the variable currently points to which in your case is the player1 object. This "pointer" then is assigned to a variable in you constructor. So after that call you have two variables that point to the player1 object. The variables itself are independent of each other.
If you then change where the currentPlayer variable points to you won't affect the other variable.
On the other hand if you modify the object the currentPlayer variable points to (e.g. player1.setName()) would be also visible to your variable in the Rules class.
To change where the variable in the Rules class points to you have to reassign that variable (e.g. via a new method rulez.setCurrentPlayer(player2)).

Turismo