Text and string templates
Building text with templates, dropping an expression into a string with braces, and the everyday string names worth knowing.
A template puts values inside text
A string in double quotes may contain a dollar sign followed by a name, and Kotlin puts the value of that name into the text before printing. val name = "Ada" and "Hello, $name!" print Hello, Ada!, with no plus signs and no counting of spaces. The name is looked up when the string is built, so a value changed before that line is the value the template sees.
Joining strings with plus still works — "Hello, " + name says the same thing — and it is the right tool when the pieces are separate. Readability is the whole argument for templates: the text keeps its shape, and the values sit in the places they belong. With plus signs, every added space has to be written and counted; with a template, the sentence is the sentence.
When the hole needs a calculation
A name works on its own, but an expression needs braces: a dollar sign, then curly braces holding the expression, so that Kotlin knows where the code stops and the text starts. val items = 3 and a price of 4.5 print a total of 13.5 with the curly-brace form. The braces are not decoration: they are the boundary between the text and the code.
Without the braces the compiler would read the first letter as part of a name that does not exist, which is why the braces are required the moment you ask for arithmetic, a function call or a property. "Total: $items * $each" prints the numbers and the asterisk, not the product — a mistake that looks like arithmetic and produces a sentence.
The string methods you will use weekly
A string carries its own tools. length counts the characters, uppercase() and lowercase() change the case, trim() removes the spaces at both ends, contains and startsWith ask a question and answer with a Boolean, and split breaks the text into a list of pieces. These names are properties or functions depending on the tool, which is why length has no parentheses while uppercase() does.
None of these change the original string: they hand back a new one. That is why the examples below often print the call directly, rather than storing a value they do not need again. When you do need the cleaned text twice, give it a name; calling trim() on every line works but hides that the same work is being repeated.
A template is a small program
A template is evaluated from left to right while the string is built. Fixed text is copied as it stands, and each hole is replaced by the value of the name or the result of the expression inside it. A hole can call a function, so a template can ask for the length of a name and place the number in the sentence without a separate line.
The result is an ordinary String, which means it can be stored, compared or printed like any other. Nothing about a template is special at run time; the compiler turns it into the same joins you would write with plus signs. That is why a template cannot change a value: it only reads the value and places its text into a new string.
Templates, and the text that follows
fun main() {
val first = "Grace"
val last = "Hopper"
val rides = 3
val fare = 2.5
println("$first $last")
println("Plus works too: " + first + " " + last)
println("Total: ${rides * fare}")
val parts = "a b c".split(" ")
println("Words: " + parts.size)
println("Letters: " + (first + last).length)
println("Padded: [" + " x ".trim() + "]")
}Building a line of text
Run it and read how each line was built. Then add a line that prints the two names separated by a comma, and one that prints the number of items multiplied by the price each so you see the braces do the arithmetic first.
Strings do not change; they are replaced
Every string method hands back a new string. trim() does not remove spaces from the value you called it on; it returns a shorter string, and the original stays exactly as it was. That is why val clean = raw.trim() is the usual shape: the text you started with is still there if you need it, and the cleaned version has its own name.
A template is compiled into the same work you would write by hand: the fixed pieces and the values are joined into a new string. $name is shorthand for that join, and ${...} marks a piece of code whose result is joined in. Nothing is printed until the whole string exists, which is why an expression inside braces is evaluated first.
Three string mistakes are worth recognising. A template needs braces as soon as it contains an operator or a call: "Total: $items * $each" prints the numbers and the asterisk, not the product. A literal dollar sign must be escaped as \$, or Kotlin reads the next word as a name. And trim() returns a new string, so ignoring its answer leaves the original spaces in place.
Try these next
- Print a name with its first and last letters, using
first()andlast(). - Build a sentence with three templates, then rebuild it with plus signs and compare the reading.
- Split a date written as
2026-10-29on the dash and print the three parts.
A badge built from untidy text
Run it and follow how the raw text becomes the clean line. Then change the name to one with three parts, print the number of parts, and make the badge show the last name first.
A string is text you can print but never do arithmetic on. If a number arrives as text, convert it first — the compiler will not add a string to an Int, and that refusal is what stops a wrong total from printing quietly.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
What does a dollar sign followed by a name do inside a string?
