0
class OtherObject {
    String innerString;
    OtherObject(String x) {innerString = x;}
}

class Playground {
    public static void mutate(OtherObject toMutate) {
        toMutate.innerString = toMutate.innerString.substring(toMutate.innerString.length()/2);
    }

    public static void mutate(String toMutate) {
        toMutate = toMutate.substring(toMutate.length()/2);
    }

    public static void main(String[] args) {
        String helloWorld = new String("Hello, World!");
        OtherObject helloWorld2 = new OtherObject("Hello, World!");
        mutate(helloWorld);
        mutate(helloWorld2);

        System.out.println(helloWorld);
        System.out.println(helloWorld2.innerString);
    }
}

In this example, I have set two objects through method mutate and only one of the objects changed, as shown in this output:

Hello, World!
 World!

Process finished with exit code 0

This confuses me because if I remember correctly:

  • Objects, when passed into methods are passing references, therefore even without returning, the object itself can be changed
  • This is the same reason I can pass an ArrayList into a method and manipulate it without having to return the same ArrayList and assigning it to the previous ArrayList in main.
  • String is an Object.

Why didn't the String helloWorld change?

3 Answers3

2

You appear to be asking about this:

public static void mutate(String toMutate) {
    toMutate = toMutate.substring(toMutate.length()/2);
}

The toMutate variable is a local variable containing a reference to a string object. Assigning to it only changes the local variable. It does not change the variable in the calling method where the references came from.

The technical answer is that Java argument passing is "pass by value" and not "pass by reference". When you pass a string, you are passing the string reference by value. You are NOT passing a reference to the variable that holds the string reference in the caller. You CANNOT do that in Java.

In short, it is not possible to implement void mutate(String) in Java with the semantics that you seem to want. The method signature needs to be different, and the semantics and usage will be different.


Objects, when passed into methods are passing references, therefore even without returning, the object itself can be changed.

There is your mistake. An object can be changed provided that it is designed to be changed. A String object is immutable. It is specifically designed so that you CANNOT change it.

If you wanted a "string like" object in Java that can be changed, you could use a StringBuilder or StringBuffer object. (Refer to the javadocs for the details, and the differences between them.)

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
0

You need to think about exactly what is being changed in those examples.

When you write

toMutate.innerString = toMutate.innerString.substring(toMutate.innerString.length()/2)

you are changing the field innerString of an instance of OtherObject. This is what it means to mutate an object; change its fields.

On the other hand, when you write

toMutate = toMutate.substring(toMutate.length()/2);

you are not changing a field of any object. All you are doing is reassigning a variable. Because Java is always pass by value, even when it's an object reference being passed, this line doesn't have any effect.

Paul Boddington
  • 37,127
  • 10
  • 65
  • 116
-1

Because Strings are immutable in Java. That is, once created, the same String object cannot be mutated. Internally, Java uses java.lang.StringBuffer to make changes to the string rather than making changes in the String object itself.

This is because, Strings once created in Java, cannot be changed in itself. A separate String object is created in the mutate(String) method. And your original String persists.

Aditya Singh
  • 2,343
  • 1
  • 23
  • 42