Вопрос о передаче параметров Java - PullRequest
2 голосов
/ 11 января 2010

Я относительно новичок в Java и во время тестирования некоторого кода наткнулся на что-то, что меня удивило. Надеюсь, кто-то может пролить свет на это. Вот некоторые фрагменты кода, относящиеся к моему вопросу. Те, кто читал практику программирования, могут найти это знакомым.

Эта функция определена для типа данных List и применяет переданный аргумент "fn" (который является не чем иным, как функцией, заключенной в объект) ко всем членам списка.

    public void apply(MapFunctionI fn, Object fnArg) {
    Node curr = head;
    for (; curr != null; curr = curr.getNext()){
        fn.function(curr.getItem(), fnArg);
    }
}

Далее я пытаюсь использовать эту функцию для подсчета количества элементов в списке, используя класс, который реализует MapFunctionI (интерфейс, для которого требуется метод с именем 'function')

class CounterFunction implements MapFunctionI {
public void function(Object node, Object arg){
    ((MyInteger)arg).increment();
}
}

Вот как я это называю.

    static void TestApply(LinkedList l){
    System.out.println("Number of elements in List -> ");
    MyInteger n = new MyInteger(0);
    l.apply(new CounterFunction(),n);
    System.out.println(n.value());
}

А вот тип MyInteger.

class MyInteger {
private int n;

MyInteger(int i){
    n = i;
}

public void increment(){
    n++;
}

public int value(){
    return n;
}

Теперь, если вам интересно, почему я использую свой собственный тип Integer, это то, с чем связан мой вопрос. Я попытался использовать для этого тип Java Integer, но я не смог заставить его работать, напечатанный ответ всегда был O, значение «Integer» не сохранялось при нескольких вызовах. Вот как я это делал,

arg = ((Integer)arg).intValue() + 1;

Чем объясняется такое поведение?

И я уверен, что есть более лаконичный способ поставить этот вопрос: -)

Ответы [ 5 ]

6 голосов
/ 11 января 2010

Первая проблема заключается в том, что тип Integer является неизменным; однажды созданная с определенным целочисленным значением, это значение не может быть изменено. Таким образом, вы не можете напрямую изменить Integer (я не говорю, что вы этого не знали или не пытались).

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

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

2 голосов
/ 11 января 2010

Прежде всего, класс Java Integer является неизменным; это только когда-либо представляет единственное целочисленное значение. Когда вы вызываете intValue() для него, это просто возвращает текущее (неизменяемое) значение целого числа.

Когда вы назначаете увеличенное значение (распакованный и перезаписанный) в arg, arg - это просто локальная ссылка на то, что было передано (например, как значение указателя внутри функции в C, скажем). Установка чего-либо с последующим возвратом из функции теряет новое значение, которое вы только что дали.

Если бы вы были в C / C ++, именно здесь вы бы использовали указатель на указатель. Но у Java нет эквивалентной конструкции, так что вы сами справляетесь с ней - такой объект-обертка, какой вы использовали, является разумным решением вашей проблемы.

2 голосов
/ 11 января 2010

Причина, по которой он не работал при использовании типа Integer, заключается в том, что вы обновляли локальную копию указателя arg внутри function(). В Java все не примитивы являются «ссылочными типами», и все аргументы функции передаются по значению.

Редактировать - если последнее утверждение сбивает с толку, представьте, что вы работали с диалектом C ++, который позволял передавать ссылки только по значению (т. Е. Вы могли передавать только копии указателей), и Вы хотели реализовать функцию swap. Не возможно, верно? Обновление локальных указателей не повлияет на указатели, которые были переданы в функцию.

1 голос
/ 11 января 2010

Это происходит потому, что при передаче параметров Java значение BY VALUE. Обратите внимание, что когда вы передаете объект в функцию, вы передаете BY VALUE его ссылку ... Или, иначе говоря, вы передаете его адрес по значению.

Когда вы пишете:

arg = .... // a new Integer Object

вы меняете значение ссылки arg локально в функции (чтобы указать на новый объект), в то время как ссылка, сохраненная в переменной fnArg в функции List.apply, остается неизменной (и, таким образом, указывает на то же целое число (0)).

Если вы знаете C ++, это то же самое различие, которое у вас есть в функции с int * или int * & в качестве параметра: в Java это похоже на наличие int * (для объектов).

0 голосов
/ 11 января 2010

Проблема с последней частью кода заключается в том, что переменная arg просто содержит ссылку на класс Integer. Когда вы устанавливаете arg на что-то, его значение получает в локальном контексте замену новым объектом и не обновляет уже существующий объект. Вы должны указать полный путь к обновляемой ссылке.

...