Start learning free

LESSON 58 OF 348 · STRINGS I

String Immutability in Kotlin: Why You Can't Change a Character

Lesson Read the full explanation

In Kotlin, strings are immutable, which means that they cannot be altered once created.

For example, the following code:

val message: String = "Jello world"

message[0] = 'H'

would produce an error.


Variable can still be changed:

Note that this does not mean that message cannot be changed, just that the individual characters of a string literal cannot be changed.

"Jello world" is a string literal that Kotlin stores in memory, while message is a variable that refers to that string literal.

When we do message[0] = 'H', we are trying to alter the string literal "Jello world", which is not allowed.

But like any other variable, message can be changed by reassigning it with a new string, like this:

var message: String = "Jello world"

message = "Hello world"

println(message)

This code would output:

Hello world

Instead of trying to alter the first string, this code now creates a new one ("Hello world"), stores it in memory and tells message to refer to this new string.


Assignment
Follow the Coding Tutorial and let's practice with string immutability!


Hint
Look at the examples above if you get stuck.


STEP 1 OF 1
    Ctrl + Enter to check

    KOTLIN · STRINGS I

    String Immutability in Kotlin, explained

    What immutable means

    A Kotlin String is immutable: once it exists, none of its characters can be changed, added or removed. Every operation that seems to modify a string, such as joining, slicing or replacing, actually builds a new string and leaves the old one as it was.

    Reading a character is fine, using square brackets with an index. Writing one is not:

    val message: String = "Jello world"
    
    message[0] = 'H'
    

    This code does not compile. The compiler reports:

    error: no 'set' operator method providing array access.
    

    Square brackets on the left of = would need a set function on String, and String has none. It has a get function, which is why message[0] works for reading.

    The variable is not the string

    Immutable strings do not mean a variable can never hold different text. There are two separate things:

    • the string, a block of characters in memory, which never changes;
    • the variable, a name that refers to one string at a time.

    Declared with var, the variable can be pointed at a different string:

    var message: String = "Jello world"
    
    message = "Hello world"
    
    println(message)
    
    Hello world
    

    Nothing inside "Jello world" was edited. A second string, "Hello world", was created and message now refers to it.

    val versus var

    Two different restrictions are easy to mix up. val stops you from reassigning the variable. Immutability stops you from editing characters. For a string, the character-editing line fails in both cases, but reassignment works only with var:

    val a = "Jello"
    a = "Hello"   // error: 'val' cannot be reassigned
    
    var b = "Jello"
    b = "Hello"   // fine
    

    The exercise below uses var for this reason.

    The exercise

    The starter code holds a typo. The variable name contains "bndrei Chiriac", and the line name[0] = 'A' tries to fix the first letter, but it does not compile for the reason above.

    The fix works in two moves:

    1. Build the corrected text. Take everything after the wrong first character with name.substring(1), which gives "ndrei Chiriac", and put the right letter in front with +. Joining two strings with + creates a new string.
    2. Assign it back. Because name was declared with var, it can now refer to the new string.
    var name: String = "bndrei Chiriac"
    
    // Change code below:
    
    name = "A" + name.substring(1)
    

    After that line, name holds "Andrei Chiriac". The old string, with the typo, is no longer referred to by anything.

    Other ways to get a modified copy

    The same idea, build a new string and keep it, applies to every change:

    val s = "Jello"
    
    println(s.replaceRange(0, 1, "H")) // Hello
    println(s.replace('J', 'H'))       // Hello
    println(s.uppercase())             // JELLO
    println(s)                         // Jello
    

    The last line shows that s never changed. If you want the new text, you have to catch the return value, either in a new variable or by assigning to a var.

    Common mistakes

    • Calling a method and ignoring the result. Writing name.replace('b', 'A') on its own line does nothing useful, because the new string is thrown away and name keeps its old value.
    • Declaring the variable with val and then reassigning. The compiler rejects it. Use var when the variable must point to new text.
    • Building a long string character by character in a loop with +. Every + creates a new string, which copies all the previous characters. For many small additions, a StringBuilder is the tool made for editing text in place, then toString() gives the final string.
    • Thinking the length changed. The slicing and joining in the exercise leave the length the same, because one character was replaced by one character. The point is that the result is a different string, not an edited one.

    Key takeaways

    • Strings are immutable: text[i] = 'x' does not compile.
    • String operations return new strings; the original stays the same.
    • A var can be reassigned to a new string, while a val cannot.
    • To "change" one character, join the parts around it with the new character, for example "A" + name.substring(1).
    Keep going with KotlinAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free