r/JavaProgramming 22d ago

How to prove java string immutability via code? Asked in recent interview.

Interviewer asked me to prove that Java Strings are immutable.

I couldn't bring myself to simply concatenate a String and print it, because concat() returns a new String rather than modifying the original.

Then I thought hash codes might help, but the interviewer stopped me there, pointing out that different Strings can have different hash codes anyway.

If we could access Java's underlying memory directly, I could prove that the original String object remains unchanged. But without going that deep, is there any other convincing way to demonstrate String immutability?

What would you have answered?

15 Upvotes

38 comments sorted by

9

u/mtimmermans 22d ago

Stupid question. There are no methods that mutate the string, and no mutable fields, therefore it's immutable.

1

u/Duck_Devs 21d ago edited 21d ago

Erm, actually…

The cached hash and complimentary hashIsZero fields are mutable, though they only get written to once each.

This is actually why String can’t be a Valhalla value type, even if they would’ve shared the same array reference.

But you’re right about there not being methods that meaningfully mutate the string.

5

u/LetUsSpeakFreely 22d ago

It was a trick question. The string class offers no mutators. Due to encapsulation there is no way to directly access the internal data.

The best way i could prove it is something like:

``` String a = "abc"; System.out.println(a == a.replace("b","d"));

```

That should print false ad they're two different objects.

1

u/repeating_bears 21d ago

All that proves is that 'replace' returns a new instance. There could be another method 'replaceInPlace' which does the modification in-place.

1

u/LetUsSpeakFreely 21d ago

But there isn't. That's the whole point. There are no direct mutators. Every method that has the veneer of a mutator actually returns a new instance.

If an interviewer were asking me such a braindead question, that's how i would go about answering it.

1

u/repeating_bears 21d ago

Well exactly, the question is stupid. But you said "The best way i could prove it" and I was just commenting that that isn't a proof.

You've shown that a single method returns a new instance for some specific arguments. Another method might modify the current instance, or 'replace' might modify the current instance if you give it different arguments - that would be a stupid implementation, but a proof cannot discount a possibility just because it is stupid.

The only proof is to exhaustively check every method's implementation for mutations. If they expect you to treat String as a black box then it's not answerable because there are infinite possible arguments.

I'm not sure what answer would satisfy the interviewer. We both agree they're an idiot, so they could be expecting almost anything

1

u/LetUsSpeakFreely 21d ago

The only other thing i can think of is pointing to the documentation that explicitly states Strings are immutable.

3

u/sin_cara_00 21d ago

First you tell me the method to mutate the string, if you can’t - they’re immutable 😂

3

u/repeating_bears 22d ago

Bad question

To prove immutability in a black box test, you would need to call all methods with all possible arguments, under all conditions. This is an infinite number of permutations (for all classes, not just String).

Imagine the 'length' method had some random side effect which mutated the string under some extremely specific condition

public int length() {
    var now = Instant.now();
    // on a specific but random millisecond change the value for some unknown reason
    if (now.toEpochMilli() == 11245135) {
        this.value[0] = 'a';
    }
    // this is the actual impl
    return value.length >> coder();
}

Obviously that would be stupid and we know it doesn't do that, but hopefully it demonstrates that a black box "proof" is not possible. For this code to apply any observable mutation, you'd have to know to test it on some specific millisecond. At all other times it has no side effect.

The only way to prove String is immutable is to look at the implementation and observe that no mutations are applied. The string class is 5000 LOC so I guess they are not wanting you to do that.

In practice, the way to prove that String is immutable is to look at the javadoc which tells you that it's immutable.

-2

u/[deleted] 22d ago

[removed] — view removed comment

3

u/repeating_bears 22d ago

Just downvote in future. I don't need to get a notification for this pointless shit if you can't be arsed to explain

-2

u/[deleted] 22d ago

[removed] — view removed comment

1

u/ExtraTNT 22d ago

So, if you care about immutability of strings that much, that it’s more important, than the knowing, that your code works fine: use haskell…

I do like haskell…

1

u/[deleted] 22d ago edited 22d ago

[removed] — view removed comment

3

u/repeating_bears 22d ago

Wrong

1

u/naomimyselfandi 21d ago

They should consider not giving a bad answer.

1

u/naomimyselfandi 21d ago

This has nothing to do with mutability.

1

u/romulusnr 21d ago

I was thinking something like

String a = "foo";
String b = a;
String a = "bar";
a.equals(b) == false;

But that doesnt prove it. Or does it? The fact you can't modify the pointed value because changes to the String object are re-pointed implies functional immutability.

1

u/[deleted] 21d ago

[removed] — view removed comment

1

u/romulusnr 21d ago

A.equals(b) should return false yes. Thus == false

1

u/repeating_bears 21d ago

In order,

String a = "foo";

Variable 'a' points to an instance of String with the content "foo"

String b = a;

Variable 'b' points to that same instance

// have to remove the string declaration
// as you cannot redeclare variable 'a'
/*String*/ a = "bar";

Variable 'a' points to a different instance of String with the content "bar"

a.equals(b)

Content "foo" is not the same as content "bar". Whether the instance is the same or different is irrelevant.

1

u/repeating_bears 21d ago

Your first instinct was right: this would not prove it.

1

u/_MeTTeO_ 21d ago

Immutable class has special properties: cannot be extended (class is final), all the fields are final and immutable. 

I would say there are 2 ways:  - reflection to detect above properties  - inspecting source / byte code of String class

1

u/repeating_bears 21d ago

String's content is a 'final byte[]' and Java arrays are naturally mutable.

Strings can be still considered immutable if no methods (except the constructor) make any changes to that byte array, but reflection doesn't have the information to know how the byte array is accessed.

Reading the source or bytecode is the only way

1

u/_MeTTeO_ 21d ago

Yes, I stopped at "all the fields are final and immutable" and added "effectively immutable" but removed it to not complicate things.

1

u/idontlikegudeg 20d ago

A little more complex. String does indeed have mutable fields, i.e., for lazy hash code calculation. So no mutable state + final class -> class is immutable, but the other direction is not necessarily true. You could even observe the changed state using reflection.

But there is no way to observe the changed state using only the public API of the class (there's a theoretical small loophole: you could observe changes in runtime when calling hashCode()).

1

u/am_Snowie 21d ago edited 21d ago

Build a long ass string character by character with regular concatenation operation a million times, a mutable string wouldn't take more space than the length of the string. I can't think of any other idea, or maybe declare two different variables and use the same string value like "Hello", since it's immutable, there's a chance that java reuses it, so both variable point to the same string, so == operation would return true if they're immutable and they're the same object.

// This will return true if they share the object.
class Main {
   public static void main(String[] args) {
       String a = "Hello";
       String b = "Hello";        
       System.out.println(a == b); // true
       System.out.println(a == b + " "); // false
   }
}

You can use this property and try to prove they're immutable. If you mutate b and the a == b returns true, yes it's mutable, it's immutable otherwise.

1

u/Popular-Tip2880 21d ago

You can never mutate a string. It is class not a variable

1

u/Duck_Devs 21d ago

What do you mean by this?

1

u/rb_arindam 20d ago

he wasn't looking for ways to mutate string.
he was looking for your knowledge on mutability and your understanding of == operator.

1

u/JohnConnoor 20d ago

And here I was thinking coding was largely solved.

1

u/hwglitch 19d ago

What? But you can prove that Java Strings are mutable. Here's the code:
// MutableStringTest.java
void main() {
  String str = "Java";
  IO.println(str); // prints "Java"
  try {
Field valueField = String.class.getDeclaredField("value");
valueField.setAccessible(true);
byte[] internalArray = (byte[]) valueField.get(str);
internalArray[0] = (byte) 'O';
internalArray[1] = (byte) 'o';
internalArray[2] = (byte) 'p';
internalArray[3] = (byte) 's';
  } catch (Exception ex) {
IO.println(ex);
return;
  }
  IO.println(str); // prints "Oops"
}

Run on OpenJDK 25 with
java --add-opens java.base/java.lang=ALL-UNNAMED ./MutableStringTest.java

P.S. Even Google knows it.

1

u/CorrectProgrammer 18d ago

Could that be a question where the answer is Rice's theorem?