views:

129

answers:

12

Java is a 'pass by value' language, meaning that sending in a variable into a method, pointing the variable to a new object, does not effect the outer variable.

public void one() {

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

}

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

Would write "one".

Is there a way to prevent this? Or what is the most common solution or pattern to actually change s to "two" inside the method?

+1  A: 

Create an object, which contains the string, then pass that into the method.

public class StringHolder {
    public String s;

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

Then the code would look like:

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

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

Although, for the above example, you could just return the value you want:

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

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

And for Strings, you can always use StringBuffer, which is mutable:

public void two(StringBuffer buf) {
    buf.setLength(0);
    buf.append("two");
}
Chris B.
That would require a class per wrapper, you could use a generic wrapper to achieve the same effect.
OscarRyz
Yes, but it really depends on the situation. If you're _only_ using Strings as input/output parameters, you don't need to muck about with Generics. Or, better, maybe you're bundling several pieces of data together and passing them as a single argument into the function. We really can't tell what the situation is from the question as asked.
Chris B.
+1  A: 

If s were a mutable object, you could change its value (i.e. the value of its data members). And the member can be a String too. This doesn't work with a String parameter directly, as it is immutable, so the only way to "change" it is to direct the reference to a different object.

Péter Török
A: 

Strings are immutable... otherwise, though, all you would have to do is call a method to operate on the same object and have that somehow change the string value.

I'm sure there are a number of Java classes that will do the job for you, but you could also roll your own simply by creating an encapsulating class with either a public field or a setter/getter. An example of the former is something like this:

public class EncapsulatedString
{
    public String str;

    public EncapsulatedString(String s)
    {
        str = s;
    }
}
Platinum Azure
A: 

You're not passing by value. You're still passing by reference. Consider the code you put. Java passes references by value.

If you had

public two(MyClass myObj) {
    myObj.setNumAsString("two");
}

that would work just fine. Or if it was public, simply

public two(MyClass myObj) {
    myObj.s = "two";
}
glowcoder
@downvoter, care to explain why?
glowcoder
If it's regarding the `passes references by value` comment, this article can explain what I mean very well. http://javadude.com/articles/passbyvalue.htm
glowcoder
A: 

1-length array is a bit ugly, but perfectly working solution. No need to create surplus wrapper classes:

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

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

This pattern is especially useful when translating old (e.g. C) code where pass by reference has been extensively applied.

That said, returning a new, fresh value is always less confusing than mutating the "input" value.

Joonas Pulakka
+1  A: 

You can't pass-by-reference - at least not the variable itself. All parameters are passed by value. However, objects contain references - and are represented as references themselves. You can always change the insides of the object, and have the changes stick. Thus, send an array, or create a wrapper class, or make your own reference object:

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

As the others have said, String is not mutable anyway, so you're not actually changing the string here, but making the variable point the other way, so it does not really make that much sense not to simply return the new string.

Amadan
A: 

Create a wrapper that contains your object and change contents of the wrapper:

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");
}
Juha Syrjälä
+1  A: 

You can't prevent Java from passing by value; that's the language semantics.

You can, one way or another, get around it, depending on what you want to do.

You can return a new value based on the parameter that's passed:

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"

You can also pass something that is mutable:

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"
polygenelubricants
A: 

Java is not a pass-by-value language. On the contrary - it is a pass-by-reference language. (passing-by-reference does not mean you can change the original "pointer" to point elsewhere like C++ allows). The only things which are passed by value are primitives (int, long, char etc.)
Object references are always passed by reference - so if your Object is able to support change to its contents (e.g. via getter and setter methods) - it can be changed.

String specifically is immutable - meaning that its contents may never be changed. So for your specific question, if you want the String referred to by the local variable 's' to change you need to provide it with a reference to a new instance of a String object.

Example:

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

If you use objects other than String - you can define setter methods on them that will update their contents for you.

RonK
Why a vote down with no comment?
RonK
Java *is* a pass-by-value language. References are passed by value, too. Granted, it may be a bit confusing, but that's how it is.
Joonas Pulakka
A: 

One ugly solution that comes to my mind is to pass around a String array of length 1. It's not something I would encourage but if you feel that you want to do it...

  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 }
jonalv
+2  A: 

Is not possible to prevent it.

You can emulate it with a generic wrapper like this:

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

And then use it as you intended.

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

But looks ugly. I think the best would be to change the reference it self:

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

And use it

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

:)

OscarRyz
In a Java code review, if I'd catch someone calling a class `_`, I'd send him back to his desk immediately.
Jesper
@Jesper, You know what? I would do the same for the attribute `public T _;` Looks interesting anyway isn't?
OscarRyz
A: 

Use StringBuffer, or StringBuilder, which are mutable.

orbfish
And if we're not talking strings, Apache provides a MutableLong, etc.
orbfish