Можно ли предотвратить передачу Java по значению в методах? - PullRequest
1 голос
/ 01 июля 2010

Java - это язык «передачи по значению», что означает, что отправка переменной в метод, указывающая переменную на новый объект, не влияет на внешнюю переменную.

public void one() {

String s = "one";
two(s);
System.out.println(s);

}

public void two( String s ) {
s = "two";
}

Напишет "один".

Есть ли способ предотвратить это? Или что является наиболее распространенным решением или шаблоном для фактического изменения s на «два» внутри метода?

Ответы [ 11 ]

2 голосов
/ 01 июля 2010

Невозможно предотвратить это.

Вы можете эмулировать его с помощью универсальной обертки, подобной этой:

class _<T>{
    public T _;
    public _(T t ) {
        _ = t;
    }
    public String toString(){ return _.toString(); }
}

И затем использовать ее по назначению.

class GeneriWrapperDemo {
    public static void main(String [] args ) {
        _<String> one = new _<String>("One");
        two( one );
        System.out.println( one );
    }
    public static void two( _<String> s ) {
        s._ = "two";
    }
}

Но выглядит безобразно.Я думаю, что лучше всего было бы изменить ссылку на себя:

public String two( String a ) {
     return "two";
}

И использовать ее

 String one = "one";
 one = two( one );

:)

1 голос
/ 01 июля 2010

Вы не можете передавать по ссылке - по крайней мере, не сама переменная. Все параметры передаются по значению. Однако объекты содержат ссылки - и представлены как сами ссылки. Вы всегда можете изменить внутреннюю часть объекта, и использовать изменения. Таким образом, отправьте массив, или создайте класс-оболочку, или создайте собственный ссылочный объект:

class Ref<T> {
  T obj;
  public Ref(T value) {
    this.obj = value;
  }
  public void set(T value) {
    obj = value;
  }
  public T get() {
    return obj;
  }
}

Как уже говорили другие, String в любом случае не является изменяемым, так что вы на самом деле здесь не изменяете строку, а переводите переменную в другую сторону, так что на самом деле не имеет большого смысла не просто возвращать новая строка.

1 голос
/ 01 июля 2010

Вы не можете запретить передачу Java по значению; это семантика языка.

Вы можете, так или иначе, обойти , в зависимости от того, что вы хотите сделать.

Вы можете return новое значение на основе переданного параметра:

static String scramble(String s) {
    return s.replaceAll("(.*) (.*)", "$2, $1");
}

// then later...
String s = "james bond";
s = scramble(s);
System.out.println(s); // prints "bond, james"

Вы также можете передать что-то изменчивое:

static void scramble(StringBuilder sb) {
    int p = sb.indexOf(" ");
    sb.append(", ").append(sb.substring(0, p)).delete(0, p+1);
}

// then later...
StringBuilder sb = new StringBuilder("james bond");
scramble(sb);
System.out.println(sb); // prints "bond, james"
1 голос
/ 01 июля 2010

Если бы s был изменяемым объектом, вы могли бы изменить его значение (то есть значение его членов данных). И член может быть String тоже. Это не работает с параметром String напрямую, поскольку он неизменен, поэтому единственный способ «изменить» его - это перенаправить ссылку на другой объект.

1 голос
/ 01 июля 2010

Создайте объект, который содержит строку, затем передайте ее в метод.

public class StringHolder {
    public String s;

    public StringHolder(String s) {
        this.s = s;
    }
}

Тогда код будет выглядеть так:

public void two(StringHolder sh) {
    sh.s = "two";
}

StringHolder sh = new StringHolder("one");
two(sh);
System.out.println(sh.s);

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

public String two(String s) {
    return "two";
}

String s = "one";
s = two(s);
System.out.println(s);

А для String s вы всегда можете использовать StringBuffer, который является изменяемым:

public void two(StringBuffer buf) {
    buf.setLength(0);
    buf.append("two");
}
0 голосов
/ 01 июля 2010

Используйте StringBuffer или StringBuilder, которые являются изменяемыми.

0 голосов
/ 01 июля 2010

Одно уродливое решение, которое приходит мне в голову, - обойти массив String длиной 1. Я бы не поощрял это, но если вы чувствуете, что хотите это сделать ...

  1  public class One {
  2 
  3     public static void main( String[] _ ) {
  4         String[] s = {"one"};
  5         two(s);
  6         System.out.println(s[0]);
  7     }
  8 
  9     public static void two( String[] s ) {
 10         s[0] = "two";
 11     }
 12 }
0 голосов
/ 01 июля 2010

Java - это , а не язык передачи по значению.Наоборот - это язык передачи по ссылке .(передача по ссылке не означает, что вы можете изменить исходный «указатель» так, чтобы он указывал в другом месте, как это позволяет C ++).Единственными вещами, которые передаются по значению, являются примитивы (int, long, char и т. Д.)
Ссылки на объекты всегда передаются по ссылке - поэтому, если ваш объект способен поддерживать изменение его содержимого (например,через методы получения и установки) - это можно изменить.

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

Пример:

public void one()  
{
    String s = "one";
    s = two(); // Here your local variable will point to a new instance of a String with the value "two"
    System.out.println(s);
}

public String two() 
{
  return "two";
}

Если вы используете объекты, отличные от String, вы можете определить для них методы установки, которые обновят их содержимое.

0 голосов
/ 01 июля 2010

Создайте оболочку, которая содержит ваш объект, и измените содержимое оболочки:

public class StringWrapper {
    private String str;    
    public String getString() {
       return str;
    }
    public String setString(String str){
        this.str = str;
    }
    public String toString() {
        return str;
    }
}

public void one() {
    StringWrapper s = new StringWrapper();
    s.setString("one");
    two(w);
    // This will print "two"
    System.out.println(s);
}
public void two( StringWrapper  s ) {
    s.setString("two");
}
0 голосов
/ 01 июля 2010
Массив

1-длины немного некрасив, но отлично работает.Нет необходимости создавать избыточные классы-обертки:

public void one() {
    String[] s = new String[]{"one"};
    two(s);
    System.out.println(s[0]);
}

public void two(String[] s) {
    s[0] = "two";
}

Этот шаблон особенно полезен при переводе старого (например, C) кода, где широко применяется передача по ссылке.

Тем не менее, возвращаяновое, свежее значение всегда менее запутанно, чем изменение значения «ввода».

...