29

I have this very awkward question...

void changeString(String str){
    str = "Hello world":
}

main(){
    String myStr = new String("");
    changeString(myStr);
}

When main returns, the value is still "" and not "Hello world". Why is that?

Also, how do I make it work? Let's say I want my function changeString to change the string it got to "Hello world".

Bevor
  • 8,396
  • 15
  • 77
  • 141
Yura
  • 313
  • 1
  • 4
  • 6

7 Answers7

39

Everyone explained why it doesn't work, but nobody explained how to make it work. Your easiest option is to use:

String changeString() {
    return "Hello world";
}

main() {

    String myStr = new String("");
    myStr = changeString();
}

Although the method name is a misnomer here. If you were to use your original idea, you'd need something like:

void changeString(ChangeableString str) {
    str.changeTo("Hello world");
}

main() {

    ChangeableString myStr = new ChangeableString("");
    changeString(myStr);
}

Your ChangeableString class could be something like this:

class ChangeableString {
    String str;
    
    public ChangeableString(String str) {
        this.str = str;
    }

    public void changeTo(String newStr) {
        str = newStr;
    }

    public String toString() {
        return str;
    }
}

A quick lesson on references:

In Java method everything is passed by value. This includes references. This can be illustrated by these two different methods:

void doNothing(Thing obj) {
    obj = new Something();
}

void doSomething(Thing obj) {
    obj.changeMe();
}

If you call doNothing(obj) from main() (or anywhere for that matter), obj won't be changed in the callee because doNothing creates a new Thing and assigns that new reference to obj in the scope of the method.

On the other hand, in doSomething you are calling obj.changeMe(), and that dereferences obj - which was passed by value - and changes it.

Community
  • 1
  • 1
NullUserException
  • 83,810
  • 28
  • 209
  • 234
  • maybe nitpicky, but your second solution won't work because String is a final class (you can't assign a String variable to a ChangeableString). It would probably help the OP to see how you would have to define ChangeableString. – Aaron Novstrup Aug 15 '10 at 06:25
  • @anov "you can't assign a String variable to a ChangeableString" Where am I doing that? – NullUserException Aug 15 '10 at 06:28
  • Thanks! Actually, my func does return a value, so I cant return the new string from it. As for the second solution (with the ChangeableString), thats a good one. However, I dont want to mess up my code and add this class for only a single call. That`s really annoying, if I use Java class thet does not have some "set" function built, do I really have to use this solution of creating another container to pass it? Isnt there some workaround to pass it by &?! Thats really not nice to write a container for each class that does not have a "set" or "change" function.... – Yura Aug 15 '10 at 06:28
  • @Yura Strings are immutable in Java, AFAIK there's no workaround for that. – NullUserException Aug 15 '10 at 06:30
  • @NullUserException Your original solution had `String myStr = new ChangeableString("");`. Looks good now. – Aaron Novstrup Aug 15 '10 at 06:41
  • Thanks a lot @NullUserException. I think I found a workaround, I am using a char[] instead of String. – Yura Aug 15 '10 at 06:43
  • 1
    @Yura I guess the real question we all should have been asking is _why_ you want to change the `myStr` reference. It might be better to make it a member variable of the class that contains your `main()` method. – Aaron Novstrup Aug 15 '10 at 06:45
  • @avon It was copy and pasted, I forgot to change that. – NullUserException Aug 15 '10 at 06:46
  • I'd advise against using a `char[]` if you're just doing it to avoid using the idioms of the language you're coding in. – Aaron Novstrup Aug 15 '10 at 06:46
  • @avon my code is far more complicated than the example I wrote. This is a function of another class that is called and it returns the myStr, but not as a return value ( since it already returns another value) but changes it. Just as I would have written in C++: int foo( &retParam1, &retParam2, &retParam3) – Yura Aug 15 '10 at 06:53
  • 2
    @Yura It's hard to help without seeing a more representative code sample, but from what your saying it sounds like your code needs to be refactored. Using a `char[]` (or a `String[]` that just contains one `String`!) is just a way to hack around the issue rather than correcting it. I understand the temptation to fall back on C++ idioms if that's what you're used to, but it's going to impede your progress in learning the new language. – Aaron Novstrup Aug 15 '10 at 07:16
  • @avin thank you very much. The char[] works for me and this is a very minor part of the code I dont want to waste any more time refactoring. It just doe not worth it. Thank you a lot thanks to everybody, I got it :)) – Yura Aug 15 '10 at 07:42
7

Java uses a call by value startegy for evaluating calls.

That is, the value is copied to str, so if you assign to str that doesn't change the original value.

starblue
  • 55,348
  • 14
  • 97
  • 151
4

If the changing of your String happens very often you could also assign a StringBuffer or StringBuilder to your variable and change its contents and only convert it to a String when this is needed.

Johannes Wachter
  • 2,715
  • 17
  • 17
2

Expanding a bit on NullUserException's excellent answer, here's a more general solution:

public class Changeable<T> {
   T value;

   public Changeable(T value) {
      this.value = value;
   }

   public String toString() {
      return value.toString();
   }

   public boolean equals(Object other) {
      if (other instanceof Changeable) {
         return value.equals(((Changeable)other).value);
      } else {
         return value.equals(other);
      }
   }

   public int hashCode() {
      return value.hashCode();
   }
}

Yura's original code can then be rewritten as:

void changeString(Changeable<String> str){
   str.value = "Hello world":
}

void main() {
   Changeable<String> myStr = new Changeable<String>("");
   changeString(myStr);
}

And, just for fun, here it is in Scala:

class Changeable[T](var self: T) extends Proxy;

object Application {
   def changeString(str: Changeable[String]): Unit = {
      str.self = "Hello world";
   }

   def main(): Unit = {
      val myStr = new Changeable("");
      changeString(myStr);
   }
}
Community
  • 1
  • 1
Aaron Novstrup
  • 20,967
  • 7
  • 70
  • 108
1

Because the reference myStr is passed by value to the function changeString and the change is not reflected back to the calling function.

P.S : I am not a Java guy.

Prasoon Saurav
  • 91,295
  • 49
  • 239
  • 345
  • 2
    I didn't downvote, but the phrase "the change is not reflected back" can be misleading. Some changes, depending on the semantics, _ARE_ reflected back. It's definitely not true that pass-by-value means that there are no side effects (and this is also the source of some beginner confusions, e.g. with passing array references and having its elements modifiable by a method). – polygenelubricants Aug 15 '10 at 09:36
0

Bill, I have a solution to your problem which uses a List as a pointer in java!

void changeString(List<String> strPointer ){
    String str = "Hello world";
    strPointer.add(0, str);
}

main(){
    LinkedList<String> list = new LinkedList<String>();
    String myStr = new String("");
    changeString(list);
    myStr = list.get(0);
    System.out.println( myStr );
}

This answer takes a little extra work to insert and get out the string from the list, however the final line will print "Hello world!"

I hope this can help others as well!

-Port Forward Podcast

benathon
  • 7,455
  • 2
  • 41
  • 70
0

Here's the one more solution by StringBuffer/StringBuilder worked for me.

static void changeValue(StringBuilder str){
    str.append("newValue");
}

main(){
    StringBuilder originalVal= new StringBuilder();
    changeValue(originalVal);
    System.out.println(originalVal.toString());
}
K.Andy Wang
  • 401
  • 1
  • 3
  • 14