Передача объекта в метод в Java, кажется, по ссылке (а Java по val) - PullRequest
0 голосов
/ 30 сентября 2010

Я думал, что когда вы передаете объекты в методы в Java, они должны быть по значению.

Вот мой код:

public class MyClass{
    int mRows;
    int mCols;
    Tile mTiles[][];     //Custom class

    //Constructor
    public MyClass(Tile[][] tiles, int rows, int cols) {
        mRows = rows;
        mCols = cols;

        mTiles = new Tile[mRows][mCols];
        for (int i=0; i < mRows; i++) {
            for (int j=0; j < mCols; j++) {
                mTiles[i][j] = new Tile();
                mTiles[i][j] = tiles[i][j];
            }
        }
    }

В этот момент любые изменения объекта mTiles отражаются обратно на объект плиток. Есть идеи как это исправить?

Спасибо всем.

Ответы [ 4 ]

7 голосов
/ 30 сентября 2010

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

Чтобы решить эту проблему, вы должны скопировать ваш объект и передать это новое справочное значение.

В вашем коде вы используете одно и то же значение ссылки в «плитках» и в «mTiles»

mTiles[i][j] = new Tile(); // <---this line is useless by the way
mTiles[i][j] = tiles[i][j] // because you then assign the same value here

Вы должны создать новый, например:

mTiles[i][j] = new Tile(tiles[i][j]);

Или

mTiles[i][j] = tiles[i][j].clone();

или

mTiles[i][j] = Tile.newFrom( tiles[i][j] );

Или что-то в этом роде, так что вы можете создать на самом деле новый, вместо того, чтобы использовать ту же ссылку.

Надеюсь, это поможет.

EDIT

Когда вы изменяете ссылку в pass-by-val, на оригинал это не влияет, то есть:

String s = "hi"; 
changeIt(s);
....
void changeIt(String s){ 
    s = "bye" // assigning to the copy a new ref value of "bye"
}

После этого оригинальное «привет» по-прежнему «привет». В pass-by-ref это будет "пока"

Вот несколько ссылок:

http://www.javaranch.com/campfire/StoryPassBy.jsp

Может кто-нибудь объяснить мне, в чем причина, по которой в Java передается «значение», а не «ссылка»?

Передать по ссылке или передать по значению?

http://www.javaworld.com/javaworld/javaqa/2000-05/03-qa-0526-pass.html

http://academic.regis.edu/dbahr/GeneralPages/IntroToProgramming/JavaPassByValue.htm

http://www.jguru.com/faq/view.jsp?EID=430996

В конечном итоге:

http://www.google.com.mx/search?sourceid=chrome&ie=UTF-8&q=java+is+pass+by+value

1 голос
/ 30 сентября 2010

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

1 голос
/ 30 сентября 2010

Вот несколько диагностических примеров для семантики передачи аргументов Java:

Для типа объекта:

void changeIt(String s) {
    s = "bye";
}

String s = "hi"; 
changeIt(s);
System.out.println(s);  // would print "bye" for call by reference
                        // but actually prints "hi"

Для примитивного типа:

void changeIt(int i) {
    i = 42;
}

int i = 0; 
changeIt(i);
System.out.println(i);  // would print "42" for call by reference
                        // but actually prints "0"

Фактическив обоих этих примерах присваивания в методах changeIt влияют только на локальную переменную соответствующего метода, и фактическими значениями будут «hi» и «0».

EDIT

А так как OP все еще мне не верит ... вот диагностический пример, показывающий, что Java также является вызовом по значению для изменяемых объектов.

public class Mutable {
    int field;
    public Mutable(int field) { this.field = field; }
    public void setField(int field) { this.field = field; }
    public int getField() { return field; }
}

void changeIt(Mutable m, Mutable m2) {
    m = m2;  // or 'm = new Mutable(42);' or 'm = null;'
}

Mutable m = new Mutable(0); 
Mutable m2 = new Mutable(42); 
changeIt(m, m2);
System.out.println(m.getField());  
                        // would print "42" for call by reference
                        // but actually prints "0"

В отличие от этого, пример даст одинаковый ответ как для вызова по ссылке, так и для вызова по семантике вызова.Это ничего не доказывает о семантике передачи аргументов.

void changeIt2(Mutable m) {
    m.setField(42);
}

Mutable m = new Mutable(); 
changeIt2(m);
System.out.println(m.getField());  
                        // prints "42" for both call-by reference
                        // and call-by-value

Поверьте мне, я программирую на Java более 10 лет, и я преподавал курсы сравнительного языка программирования науниверситетский уровень.

0 голосов
/ 20 октября 2012

На самом деле, Java передается по значению. Но у этого также есть "массивы по ссылке"! Вот почему многие думают, что Java является передачей по ссылке для объектов (по крайней мере, для массивов) и только передачей по значениям для примитивов.

Вот краткий тест:

String[] array = new String[10];
array[0] = "111";
ArrayList one = new ArrayList(); 
one.add(array);
ArrayList two = (ArrayList) one.clone(); //Alternate with: ArrayList two = one;
String[] stringarray1 = (String[]) one.get(0);
String[] stringarray2 = (String[]) two.get(0);
System.out.println("Array: "+one+" with value: "+stringarray1[0]);
System.out.println("Array: "+one+" with value: "+stringarray2[0]);
array[0] = "999";
String[] stringarray3 = (String[]) one.get(0);
String[] stringarray4 = (String[]) two.get(0);
System.out.println("Array: "+one+" with value: "+stringarray3[0]);
System.out.println("Array: "+two+" with value: "+stringarray4[0]);

Независимо от того, используете ли вы клон или используете =, System.out.print всегда будет выглядеть так:

Array: [[Ljava.lang.String;@addbf1] with value: 111
Array: [[Ljava.lang.String;@addbf1] with value: 111
Array: [[Ljava.lang.String;@addbf1] with value: 999
Array: [[Ljava.lang.String;@addbf1] with value: 999

Это доказывает, что клонирование и массивы являются вредной комбинацией, потому что массивы хранят только указатели! Мне все еще нужно проверить, верно ли это и для объектов без массивов ... потому что это будет означать, что Java всегда является "хранилищем по ссылке" (а функция "клон" будет плохой шуткой для любых объектов. содержит массивы), в то время как только примитивы являются реальными значениями и не имеют ссылок!

И так как мы знаем о логике: хранилище по ссылке x передача по значению == "хранение по значению x передача по ссылке" (для объектов!),

в то время как мы уже знали со школы: хранение по значению x передача по значению (для примитивов)

Итак, все ли нам лгали наши учителя по программированию (даже в университете)? Возможно, но, по крайней мере, они не делали никаких логических ошибок ... так что это не было ложью, это было просто неправильно.

EDIT

Я написал тот же код, что и выше, с классом, сначала структура данных:

public class Foobar implements Cloneable {
    String[] array;

    public Foobar() {
        this.array = new String[10];
    }

    public String getValue(){
        return array[0];
    }

    public String[] getArray(){
        return array;
    }

    public void setArray(String[] array){
        this.array = array;
    }

    @Override
    public Object clone(){
        try{
            Foobar foobar = (Foobar) super.clone();
            foobar.setArray(array);
            return foobar;
        }
        catch(Exception e){
            return null;
        }
    }
}

Теперь контроллер:

String[] array = new String[10];
array[0] = "111";
Foobar foo1 = new Foobar();  
foo1.setArray(array);
Foobar foo2 = foo1; //Alternation: Foobar foo2 = (Foobar) foo1.clone();  
System.out.println("Instance: "+foo1.getArray()+" with value: "+foo1.getValue());
System.out.println("Instance: "+foo2.getArray()+" with value: "+foo2.getValue());
array[0] = "999";
System.out.println("Instance: "+foo1.getArray()+" with value: "+foo1.getValue());
System.out.println("Instance: "+foo2.getArray()+" with value: "+foo2.getValue());

Результаты теста всегда будут выглядеть так - независимо от того, буду ли я использовать = или clone ():

Instance: [Ljava.lang.String;@42e816 with value: 111
Instance: [Ljava.lang.String;@42e816 with value: 111
Instance: [Ljava.lang.String;@42e816 with value: 999
Instance: [Ljava.lang.String;@42e816 with value: 999

Теперь у меня в кармане «мастер-массив», с помощью которого я могу управлять всеми объектами сразу! (что на самом деле не очень хорошая вещь)

Мне всегда было неловко из-за массивов Java, но я не мог сказать, что это было. Теперь я это знаю и чувствую себя хорошо, поскольку с тех пор я использовал массивы только как контейнеры для объектов ... только для того, чтобы удивляться, насколько они важны в таких языках сценариев, как PHP!

Тем не менее, массивы Java отлично подходят для синхронизации между потоками, поскольку вы можете легко передавать их и по-прежнему получать доступ к общим значениям. Но программисты, работающие на PHP, C ++ или где-то еще, действительно могут испытывать некоторые проблемы с массивами Java. ; D

О, мне нравится эта статья: http://javadude.com/articles/passbyvalue.htm

ОБНОВЛЕНИЕ: Я нашел отличное решение для копирования любого объекта, содержащего массивы, см. Мой комментарий здесь: Ошибка при использовании Object.clone ()

...