RizTech Academy logo
RizTech Academy
Language BasicsLesson 3 of 520 min

Strings and string templates

Text is most of what programs handle — names, addresses, messages, JSON. Kotlin's strings are pleasant to work with, and two features in particular — templates and raw strings — remove the noise that makes string handling ugly in older languages. This lesson covers them and the methods you will reach for daily.

String templates — stop gluing strings together

You met $name in the first program. Here it is in full. Inside a string, $ inserts a value:

val name = "Kavita"
val age = 33
println("$name is $age years old")     // Kavita is 33 years old

For anything more than a plain variable — a property, a method call, an expression — wrap it in ${...}:

val price = 199.99
val qty = 3
println("Total: ₹${price * qty}")           // Total: ₹599.97
println("Name has ${name.length} letters")  // Name has 6 letters
println("${name.uppercase()} from Pune")    // KAVITA from Pune

Compare with the string concatenation you would write in older Java — name + " is " + age + " years old" — and the template is obviously clearer. Prefer templates over + for building strings. They read like the sentence they produce, and there is nowhere to misplace a space or a +.

To print a literal dollar sign, escape it: "\$5" prints $5.

Raw strings — for paths, JSON, and anything with quotes or newlines

A normal string needs escaping for quotes, backslashes and newlines, which gets ugly fast:

val ugly = "She said \"namaste\"\nand left"   // escapes everywhere

A raw string, in triple quotes, takes the text exactly as written — no escaping, real newlines:

val greeting = """
    She said "namaste"
    and left
""".trimIndent()

Raw strings are perfect for multi-line text, file paths ("""C:\Users\Kavita""" — no doubled backslashes), JSON, SQL, and regular expressions. trimIndent() removes the common leading indentation so the string lines up with your code without carrying the code's indentation into the value — you will use it on almost every raw string. Templates work inside raw strings too:

val name = "Kavita"
val json = """
    { "name": "$name", "city": "Pune" }
""".trimIndent()

The methods you will use constantly

Strings carry a large, useful API. The ones worth knowing from the start:

val s = "  Namaste, Pune  "
println(s.length)                 // 17  (includes the spaces)
println(s.trim())                 // "Namaste, Pune"
println(s.uppercase())            // "  NAMASTE, PUNE  "
println(s.lowercase())            // "  namaste, pune  "
println(s.trim().split(", "))     // [Namaste, Pune]  — a List<String>
println("Pune".contains("un"))    // true
println("Pune".startsWith("Pu"))  // true
println("Pune".replace("P", "T")) // "Tune"
println("a,b,c".split(","))       // [a, b, c]
println("Pune".reversed())        // "enuP"

Two things to internalise. split returns a List<String>, not another string — it is your bridge from text to a collection you can process (the collections module lives on this). And every one of these returns a new string; the original is untouched, because strings are immutable. s.uppercase() does not change s — it hands you a new string. If you want the result, you must keep it:

val name = "kavita"
name.uppercase()                 // computed and thrown away!
val proper = name.uppercase()    // kept — this is what you want

Forgetting to keep the result is a real beginner bug, and it is silent — no error, the change just does not happen.

Indexing and characters

A string is a sequence of Chars, accessible by index (from zero):

val s = "Pune"
println(s[0])              // P   — a Char, in single quotes when written literally
println(s.first())         // P
println(s.last())          // e
println(s.indexOf("n"))    // 2
for (c in "Pune") print("$c ")   // P u n e

Indexing out of range throws — s[99] on a four-character string is a StringIndexOutOfBoundsException. Prefer first(), last(), getOrNull(index) and iteration over manual indexing when you can; they are harder to get wrong.

Comparing and building

Compare strings with == (value equality, from the types lesson — it does what you mean):

println("Pune" == "Pune")              // true
println("Pune".equals("pune", ignoreCase = true))  // true — case-insensitive when you ask

When you genuinely need to assemble a string in a loop, StringBuilder avoids creating a new string each time — but reach for it only when you have measured a need; for ordinary work, templates and joinToString are clearer:

val cities = listOf("Pune", "Mumbai", "Nagpur")
println(cities.joinToString(", "))            // Pune, Mumbai, Nagpur
println(cities.joinToString(prefix = "[", postfix = "]"))  // [Pune, Mumbai, Nagpur]

joinToString is the clean way to turn a list back into text, and it beats a hand-written loop with a StringBuilder for readability every time.

Check your work

How a string template inserts a plain variable, and an expression. $name for a variable; ${...} for a property, call or expression.

Why prefer templates over +. They read like the sentence they produce, with nowhere to misplace a space or +.

What a raw string (triple quotes) is for. Multi-line text, paths, JSON, SQL, regex — no escaping, real newlines.

What trimIndent() does. Removes the common leading indentation so the value does not carry the code's indentation.

What split returns. A List<String> — the bridge from text to collections.

Why s.uppercase() without keeping the result does nothing useful. Strings are immutable; every method returns a new string and leaves the original untouched.

What happens on s[99] for a short string. A StringIndexOutOfBoundsException — prefer first/last/getOrNull.

How to compare strings, including case-insensitively. == for value equality; equals(other, ignoreCase = true) when case should not matter.

The clean way to turn a list into text. joinToString, not a hand-written loop.

Practice

  1. Build a sentence with a template using a name and an age. Then rewrite it with + and decide which you prefer.
  2. Use ${...} to embed a calculation and a method call in one string.
  3. Write a multi-line raw string with quotes in it, apply trimIndent(), and print it.
  4. Call uppercase() on a string without keeping the result, print the original, and confirm it is unchanged. Then keep the result and print that.
  5. split a comma-separated string and confirm you get a List. Print its size.
  6. Index a string at position 0 and at a position past its end. Read the exception for the second.
  7. Compare two strings that differ only in case, first with == (false) then with equals(..., ignoreCase = true) (true).
  8. Turn listOf("Pune", "Mumbai", "Nagpur") into "Pune | Mumbai | Nagpur" with joinToString.

Official documentation

Next: if, when, and expressions that return values.

Stuck on this lesson?

Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.

About the internship