Как передать объект в класс, чтобы когда объект обновлялся вне класса, он тоже устаревал в нем? (Джава) - PullRequest
1 голос
/ 12 ноября 2009

Мне трудно понять, как передать по ссылке в Java. В настоящее время у меня есть класс следующим образом

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

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

В моем основном классе я инициализирую список массивов Foo и объект Bar. Затем я инициализирую пример, передавая в список массив и объекты Bar. Я хочу, чтобы всякий раз, когда объекты, которые я передавал в Example, обновлялись в классе Main, они также обновлялись в Example. Но пока обновляется только ArrayList. Но Бар не привет. Чем ArrayList отличается от Bar тем, что обновляет, а Bar нет?

Редактировать

Хорошо, вот часть действующего кода:

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;
    }
}

Когда я создаю объект Rules, я передаю ему boardboard, dummy и currentPlayer. Когда я добавляю материал в список форумов, список правил внутри Правил такой же, как и снаружи. Но в последнем утверждении, когда я меняю currentPlayer на player2, он не меняется в Правилах.

Ответы [ 7 ]

2 голосов
/ 12 ноября 2009

В Java нет передачи по ссылке, только передача по значению. Однако если в вашем списке аргументов есть класс (например, ArrayList или Bar), вы передаете ссылку на его экземпляр по значению, поэтому любые изменения, внесенные в них, будут видны после вызова метода.

Я подозреваю, что вы просто установили новую ссылку на параметр, что-то вроде этого:

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

С другой стороны, этот работает так, как вы ожидаете:

 public void thisIsOk(Bar hello) {
     hello.setFoo("new value");
 }
2 голосов
/ 12 ноября 2009

Если Bar не является примитивным типом, то любые изменения, внесенные в Bar вне вашего класса, также должны влиять на Bar в вашем классе.

Если вы пытаетесь изменить Bar путем переназначения другого объекта переменной, то изменения не будут отражены в вашем классе. (У меня такое ощущение, что это может быть в вашем коде)

Однако, если Bar является примитивным типом (int, double, char и т. Д.), Вы не можете вносить в него изменения так, как вы это делаете прямо сейчас.

решение!

В этой строке вашего кода:

currentPlayer = player2;

Вы ожидаете, что изменения будут отражены в вашем классе. Они не будут. Вы должны добавить метод changeCurrentPlayer(Player p) и переназначить текущего игрока в вашем классе.

Реализация метода может выглядеть примерно так:

public void changeCurrentPlayer(Player newPlayer){
    this.player = newPlayer;
}
1 голос
/ 12 ноября 2009

Java, как и C, имеет только проход по значению. Там нет прохода по ссылке. Ссылки передаются по значению.

Возьмите этот класс, например:

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); }
}

Если мы используем это так:

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);
    }
}

Вывод будет 7. Это показывает, что Java передает ссылку «x» по ссылке или по значению ссылки.

Если мы используем это так:

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);
    }
}

Вы получаете 42, который показывает, что Java передает ссылку по значению.

Таким образом, когда назначение выполняется где-то, оно не меняет того, на что указывает оригинал (или, в случае вашего кода, копия). Нет способа (ну, нет, хорошего способа) делать то, что вы хотите в Java.

Что вам нужно сделать, так это предоставить метод setCurrenPlayer (конечный игрок) в вашем классе Rules.

Также не будьте крутыми и называйте переменную "rulez", это просто "правила". Очень распространено делать такие вещи, как Foo Foo; или Бар-бар; при именовании переменных. Если у класса хорошее имя, то его использование в качестве имени переменной обычно хорошая идея: -)

0 голосов
/ 12 ноября 2009

Переменные на самом деле являются только ссылками на объекты, поэтому оба объекта игрока (player1 и player2) живут в памяти, а переменная currentPlayer указывает только на место в памяти, где расположен объект player1.
Когда вы переназначаете переменную как в

currentPlayer = player2;

Вы меняете только то место, на которое указывает переменная.

Когда вы передаете переменную методу, как в

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

передается только значение (а не ссылка) currentPlayer. И значение - это место, где в данный момент находится переменная, на которую в вашем случае является объект player1. Этот «указатель» затем присваивается переменной в вашем конструкторе. Поэтому после этого вызова у вас есть две переменные, которые указывают на объект player1. Сами переменные не зависят друг от друга.
Если вы затем измените местоположение переменной currentPlayer, это не повлияет на другую переменную.
С другой стороны, если вы измените объект, переменная currentPlayer, на которую указывает переменная (например, player1.setName()), также будет видна вашей переменной в классе Rules.
Чтобы изменить место, на которое указывает переменная в классе «Правила», необходимо переназначить эту переменную (например, с помощью нового метода rulez.setCurrentPlayer(player2)).

0 голосов
/ 12 ноября 2009

currentPlayer в основном классе - это не тот же объект, который находится в вашем классе Rules, они являются отдельными. Объект currentPlayer будет существовать в памяти как отдельный объект от объекта в классе Rules, поэтому вы можете захотеть добавить функцию в Rules, чтобы сменить игрока на тот, который вам нужен. Может быть, что-то вроде этого:

public void changePlayer(Player aPlayer)
{
    this.player = aPlayer;
}
0 голосов
/ 12 ноября 2009

Нет разницы между списком и приветом. Если вы вызываете какой-либо метод для внешнего объекта, который вы передали конструктору Example, вы фактически вызываете его и для члена класса. Вы не опубликовали весь свой код, поэтому я могу только догадываться, что, возможно, вы имеете в виду следующее:

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

Конечно, в третьей строке вы не «обновляете» объект hello, а изменяете его ссылку на другой. Если вы сделали что-то подобное ниже, это обновит поле члена в примере:

bar hello = new bar("abc");
Example example = new Example(list, hello);
hello.setValue("xyz");
0 голосов
/ 12 ноября 2009

Не видя больше вашего кода, невозможно сказать наверняка, но я догадываюсь, что ваша проблема в том, что вы переназначаете hello другому экземпляру после того, как передаете его в конструктор Example. Причина, по которой он работает так, как вы ожидаете для ArrayList, заключается в том, что вы вызываете методы для одного и того же экземпляра ArrayList, не переназначая фактический список, который был передан конструктору Example.

EDIT:

Хорошо, теперь, когда я увидел реальный код, который вы разместили, я вижу, что моя догадка выше верна. Когда вы делаете это:

currentPlayer = player2;

вы назначаете currentPlayer другому экземпляру. Он больше не указывает на тот же экземпляр, который вы передали конструктору Rules. То, что вы действительно хотите сделать, это что-то вроде этого:

rulez.setPlayer(player2);

Конечно, вашему классу Rules нужен метод setPlayer(Player player), определенный для этой работы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...