Welcome back! In lesson 06 you printed text and met string templates. You even used val without knowing exactly what it was. Today we open that box properly. Variables are how a program remembers things: a customer's name, the price of a bag of rice, whether the user is logged in, how many taps a button has received. Almost every line of every Android app reads or changes a variable, so this lesson is one you will use every single day.
Think about a notebook in a shop. Each page has a label at the top ("Rice", "Beans", "Garri") and a number you can read. Some labels you never change (the shop's name). Others you cross out and rewrite many times (the stock count). Kotlin has the same two kinds of label, and choosing the right one is the first habit of a good Kotlin developer.
By the end of this lesson you will be able to:
- Create variables with
val(read-only) andvar(changeable) and say when to use each. - Name variables the way Kotlin developers expect (camelCase).
- Use the basic data types:
Int,Long,Double,Float,Boolean,CharandString. - Let Kotlin infer a type, or write the type yourself.
- Use arithmetic, comparison, logical and assignment operators, including integer division and remainder.
- Convert between number types safely.
- Declare compile-time constants with
const val. - Predict and avoid classic bugs such as integer overflow and floating-point surprises.
How this fits the bigger picture: This lesson covers the official Create and use variables in Kotlin codelab and the data types, operators and type inference material in Unit 1, Pathway 1 of Android Basics with Compose. We add more practice, more pitfalls and local examples. Links to the official pages are at the end.
You only need what lesson 06 gave you. A quick refresher:
1. A program runs statements from top to bottom
Each statement finishes before the next begins. This matters today because a variable must be created before it is used. Using a label you have not written yet is an error.
2. println and string templates
println("Hello") prints a line. "Hello $name" and "Total: ${a + b}" put values inside text. We will use these in every example to show results.
3. The Playground and scratch files
Run the examples in the Kotlin Playground (play.kotlinlang.org) or an Android Studio scratch file (lesson 06 showed both). Type each example yourself instead of pasting it. Your fingers learn syntax faster than your eyes do.
4. What is memory?
When a program runs, the phone or computer keeps its data in memory (RAM), a huge row of tiny boxes. A variable is a name for one of those boxes, plus a rule about what may be stored in it. You never need to know the box's address. You just use the name.
A variable has four things:
- A keyword (
valorvar). - A name (the label).
- A type (the rule about what can go inside).
- A value (what is inside right now).
keyword name type value
| | | |
val shopName: String = "Mama Put"
var stock: Int = 40
val versus var
val |
var |
|
|---|---|---|
| Meaning | value: read-only | variable: changeable |
| Can you give it a new value later? | No | Yes |
| Analogy | The name painted on the shop signboard | The stock count on the chalkboard |
| Use when | The value never changes after being set | The value must change while the program runs |
The golden rule: start with val. Only switch to var when the compiler tells you that you really need to change it. Code with many vals is easier to reason about, because you know a value cannot change behind your back. Android Studio even shows a warning if you write var for something you never change.
fun main() {
val shopName = "Mama Put" // never changes
var stock = 40 // will change
println("$shopName has $stock plates")
stock = 35 // allowed: stock is a var
println("$shopName has $stock plates")
}
Mama Put has 40 plates
Mama Put has 35 plates
If you try shopName = "Mama Nkechi", the compiler refuses with an error like Val cannot be reassigned. That is not Kotlin being unfriendly. It is Kotlin protecting you from an accidental change.
Important detail: val means the label cannot move
A val stops you from pointing the name at something else. With simple values like numbers and strings, that also means the value is fixed. Later (lesson 14) you will meet lists whose contents can change even when the list is held by a val. For now, remember "val = cannot reassign".
Naming rules and conventions
Rules the compiler enforces:
- Names may contain letters, digits and underscores, but must not start with a digit (
2platesis illegal). - Names are case sensitive (
stockandStockare different). - You cannot use a reserved keyword such as
fun,val,iforclassas a name.
Conventions every Kotlin developer follows:
- Use camelCase: first word lowercase, each next word starts with a capital:
numberOfPlates,isLoggedIn,totalPriceInNaira. - Choose meaningful names.
platesbeatsp, andstudentCountbeatsx. - Boolean names read like questions:
isEmpty,hasPaid,canVote. - Constants use
SCREAMING_SNAKE_CASE(see theconst valsection below).
Think about it: Which is easier to understand six months from now,
val t = p * qorval totalPrice = price * quantity? Always write for the future reader. That future reader is usually you.
Every value in Kotlin has a type. The type decides what the value can be and which operations make sense. You cannot multiply a name by a name, but you can multiply a price by a quantity. Here are the types you will use most.
| Type | Holds | Example literals | Notes |
|---|---|---|---|
Int |
whole numbers | 0, 42, -7, 1_000_000 |
About -2.1 billion to +2.1 billion. The default for whole numbers. |
Long |
bigger whole numbers | 3_000_000_000L |
Needs the L suffix on the literal. About 9 quintillion at most. |
Double |
decimal numbers | 3.14, 0.5, 2.0 |
The default for decimals. Roughly 15 to 16 digits of precision. |
Float |
smaller decimals | 3.14f |
Needs the f suffix. Roughly 6 to 7 digits of precision. |
Boolean |
true or false |
true, false |
Used for decisions (lesson 09). |
Char |
one single character | 'A', '7', '₦' |
Single quotes. |
String |
text | "Ibadan", "" |
Double quotes. |
There are also Short and Byte (smaller whole-number types) that you will rarely use in everyday apps. Knowing that they exist is enough.
Underscores make big numbers readable
Kotlin lets you put underscores inside numeric literals. They are ignored by the compiler but help your eyes:
fun main() {
val population = 3_800_000 // a rough figure for Ibadan's scale
val bigNumber = 3_000_000_000L // Long: too large for Int
println(population)
println(bigNumber)
}
3800000
3000000000
Type inference: let Kotlin work it out
You do not have to write the type. Kotlin looks at the value on the right and decides:
fun main() {
val city = "Ibadan" // Kotlin infers String
val age = 24 // Int
val height = 1.75 // Double
val isStudent = true // Boolean
val grade = 'A' // Char
val price: Int = 1500 // the type written explicitly
val rate: Double = 7.5
println("$city $age $height $isStudent $grade $price $rate")
}
Ibadan 24 1.75 true A 1500 7.5
When should you write the type? Write it when the value alone does not say enough, when you want a different type than the default (val ratio: Float = 0.5f), when you declare a variable without a value yet, or when it helps readers. Otherwise let inference do the work. Kotlin is statically typed: the type is fixed at compile time. A variable that starts as an Int can never later hold text.
fun main() {
var score = 10
// score = "ten" // ERROR: type mismatch, String cannot go into an Int variable
score = 11
println(score)
}
11
Declaring first, assigning later
A variable may be declared now and given its value later, as long as you give the type and assign exactly once (for a val) before reading it:
fun main() {
val message: String
val hour = 9
message = "Good morning" // first and only assignment
println("$message, it is $hour o'clock")
}
Good morning, it is 9 o'clock
An operator is a symbol that does something to values. The values are called operands.
Arithmetic operators
| Operator | Meaning | Example | Result |
|---|---|---|---|
+ |
add | 7 + 2 |
9 |
- |
subtract | 7 - 2 |
5 |
* |
multiply | 7 * 2 |
14 |
/ |
divide | 7 / 2 |
3 (both are Int!) |
% |
remainder (modulo) | 7 % 2 |
1 |
The most surprising row is 7 / 2. When both operands are whole numbers, Kotlin does integer division and throws the fractional part away. Make one side a Double to get a decimal answer:
fun main() {
println(7 / 2) // Int / Int
println(7 / 2.0) // Int / Double
println(7.0 / 2) // Double / Int
println(7 % 2) // remainder
println(20 % 5) // exactly divisible, so 0
}
3
3.5
3.5
1
0
The remainder operator is more useful than it first looks. number % 2 == 0 tests whether a number is even. Turning a total number of minutes into hours and minutes uses both: 130 / 60 is 2 hours and 130 % 60 is 10 minutes.
Order of operations follows school mathematics: *, / and % before + and -, and parentheses first. 2 + 3 * 4 is 14, while (2 + 3) * 4 is 20. When in doubt, add parentheses. They cost nothing and make the intention obvious.
Assignment and compound assignment
= stores a value. The compound forms update a var using its own current value:
fun main() {
var balance = 5000
balance += 1500 // same as balance = balance + 1500
balance -= 700 // balance = balance - 700
balance *= 2 // balance = balance * 2
balance /= 4 // balance = balance / 4 (integer division)
println(balance)
var taps = 0
taps++ // add one
taps++
taps-- // subtract one
println(taps)
}
Working out the first number: 5000 + 1500 = 6500, minus 700 is 5800, times 2 is 11600, divided by 4 is 2900.
2900
1
Compound assignment only works on a var, because it changes the variable.
Comparison and logical operators
These produce a Boolean and drive decisions in lesson 09.
| Operator | Meaning | Example | Result |
|---|---|---|---|
== |
equal to | 5 == 5 |
true |
!= |
not equal | 5 != 5 |
false |
< > |
less / greater | 3 < 8 |
true |
<= >= |
less or equal / greater or equal | 8 >= 8 |
true |
&& |
and (both true) | true && false |
false |
\|\| |
or (at least one true) | true \|\| false |
true |
! |
not (flips it) | !true |
false |
Note that = stores and == compares. Mixing them up is a classic beginner slip. In Kotlin, == also compares the contents of two strings, so "abc" == "abc" is true.
fun main() {
val age = 17
val hasParentPermission = true
val canJoin = age >= 18 || hasParentPermission
println("Can join: $canJoin")
println("Is an adult: ${age >= 18}")
println("Not an adult: ${!(age >= 18)}")
}
Can join: true
Is an adult: false
Not an adult: true
Kotlin will not silently turn one number type into another. Each type has conversion functions: toInt(), toLong(), toDouble(), toFloat(), toString(). This looks fussy but prevents bugs where precision is quietly lost.
fun main() {
val items = 3
val pricePerItem = 250.5
val total = items * pricePerItem // Int * Double gives Double
println(total)
val wholeNaira = total.toInt() // cuts the decimals off, does not round
println(wholeNaira)
val count: Int = 5
// val big: Long = count // ERROR: no automatic widening
val big: Long = count.toLong()
println(big)
val text = "42"
val number = text.toInt() // text to number
println(number + 8)
}
Check the first line: 3 times 250.5 is 751.5. This value happens to be exactly representable in binary, so it prints cleanly. Many decimals are not, and then tiny extra digits appear (see the next section), so never rely on exact decimal output for money.
751.5
751
5
50
Two points to remember:
toInt()on aDoubletruncates.3.99.toInt()is3. To round, useroundToInt()afterimport kotlin.math.roundToInt, so3.7.roundToInt()is4."abc".toInt()crashes the program with aNumberFormatException, because "abc" is not a number. The safe versiontoIntOrNull()returnsnullinstead. You will master that in lesson 10, Nullability in Kotlin.
When you mix an Int and a Double in one calculation, Kotlin automatically gives you a Double. That is why 3 * 250.5 works without a manual conversion.
Surprise 1: integer overflow
An Int has a fixed size. Go past its maximum and the value wraps around to a huge negative number, with no error message:
fun main() {
val max = Int.MAX_VALUE
println(max)
println(max + 1)
val safe = max.toLong() + 1
println(safe)
}
2147483647
-2147483648
2147483648
If a value could pass about two billion (a bank balance in kobo, a file size in bytes, a timestamp in milliseconds), use Long.
Surprise 2: decimals are approximate
fun main() {
println(0.1 + 0.2)
println(1.0 / 3)
}
0.30000000000000004
0.3333333333333333
Double stores an approximation, so tiny errors appear. That is fine for a height or a temperature, but it is a poor choice for money. Professionals often store money as a whole number of the smallest unit (kobo or pesewas, as an Int or Long) and only format it for display. For example, ₦249.99 becomes 24999 kobo.
Some values are fixed and known before the program runs, such as a tax rate or the number of days in a week. Declare them with const val at the top level of the file (outside any function):
const val DAYS_IN_WEEK = 7
const val APP_NAME = "Techbase Notes"
fun main() {
val weeks = 4
println("$APP_NAME: ${weeks * DAYS_IN_WEEK} days in $weeks weeks")
}
Techbase Notes: 28 days in 4 weeks
A const val must be a basic type (a number, Boolean, Char or String) whose value is known when the code is compiled. A plain val can be set from something calculated while the program runs, such as the current time. Use const val for true constants. Compose projects use them for things like log tags and keys.
Let us combine everything into one small program.
Goal: Convert 30°C to Fahrenheit, and turn 400 minutes into hours and minutes.
const val FREEZING_F = 32.0
fun main() {
// Temperature: F = C * 9 / 5 + 32
val celsius = 30.0
val fahrenheit = celsius * 9 / 5 + FREEZING_F
println("$celsius°C is $fahrenheit°F")
// Time: split total minutes into hours and minutes
val totalMinutes = 400
val hours = totalMinutes / 60
val minutes = totalMinutes % 60
println("$totalMinutes minutes is $hours h $minutes min")
// Even or odd?
val number = 17
val isEven = number % 2 == 0
println("$number is even: $isEven")
}
Reasoning through the output: 30.0 * 9 is 270.0, divided by 5 is 54.0, plus 32.0 is 86.0. For time, 400 divided by 60 is 6 (integer division) and 400 remainder 60 is 40, because 6 times 60 is 360. Finally 17 divided by 2 leaves 1, so it is not even.
30.0°C is 86.0°F
400 minutes is 6 h 40 min
17 is even: false
Everything above runs in a scratch file (File > New > Scratch File > Kotlin) or a Kotlin library module, exactly as lesson 06 showed. Later, inside an Android app, variables look the same. For example, a screen that counts taps has a var that changes, and a val for the label. In lesson 17 you will write var count by remember { mutableStateOf(0) }. It is still a var, wrapped in a helper that tells Compose to redraw the screen whenever the value changes.
Scenario: You sell airtime and data bundles at a small kiosk and want a program to summarise a day's sales.
Exercise 1: Daily sales summary
Objective: Use val, var, compound assignment and a template.
Steps:
- Create
val kioskName = "Tunde Telecom". - Create
var totalSales = 0. - Add three sales to
totalSalesusing+=: 500, 1200 and 300. - Create
val target = 2500andval hitTarget = totalSales >= target. - Print
Tunde Telecom sold ₦2000. Target reached: false, with the values coming from the variables.
fun main() {
val kioskName = "Tunde Telecom"
var totalSales = 0
totalSales += 500
totalSales += 1200
totalSales += 300
val target = 2500
val hitTarget = totalSales >= target
println("$kioskName sold ₦$totalSales. Target reached: $hitTarget")
}
Expected output:
Tunde Telecom sold ₦2000. Target reached: false
500 + 1200 + 300 is 2000, which is less than 2500, so the comparison gives false. We used val for everything that never changes and var only for the running total.
Exercise 2: Predict, then run
Without running it, write down what each line prints. Then check in the Playground.
fun main() {
val a = 17
val b = 5
println(a / b)
println(a % b)
println(a / b.toDouble())
println(a + b * 2)
println((a + b) * 2)
println(a > b && b > 10)
println(a > b || b > 10)
}
3
2
3.4
27
44
false
true
Why: 17 / 5 is integer division, giving 3. 17 % 5 is 2 because 3 times 5 is 15. Dividing by 5.0 keeps the decimals, giving 3.4. Multiplication happens first in 17 + 5 * 2, so 17 + 10 is 27. With brackets, 22 times 2 is 44. a > b is true but b > 10 is false, so && gives false while || gives true.
Exercise 3: Fix the compiler errors
This program has four problems. Find them all, then fix them.
fun main() {
val price = 1500
val quantity = "3"
val total: Int = price * quantity
price = 2000
val discount: Long = 100
val finalPrice: Int = total - discount
println("Pay: $finalPrice")
}
The four problems:
quantityis aString, soprice * quantitydoes not compile. Make it a number:val quantity = 3.price = 2000tries to reassign aval. Either remove that line or changepriceto avarif it truly needs to change.val discount: Long = 100is allowed (the literal fits), but thentotal - discountis anIntminus aLong, which produces aLong.- A
Longcannot be stored in anIntvariable. MakediscountanInt, so everything matches.
fun main() {
val price = 1500
val quantity = 3
val total: Int = price * quantity
val discount = 100
val finalPrice: Int = total - discount
println("Pay: $finalPrice")
}
Expected output:
Pay: 4400
In a scratch file, create variables for your age (Int), your height in metres (Double), your first name (String) and whether you are a student (Boolean). Print one sentence using all four. Then try to assign the wrong type to one of them on purpose and read the compiler's message.
Mistake #1: Using var for everything. The wrong code: var name = "Ada" when the name never changes. The problem: nothing is wrong yet, but you lose Kotlin's protection. The fix: use val by default and let the editor's warning guide you.
Mistake #2: Reassigning a val. The wrong code: val count = 0 then count = count + 1. The error: Val cannot be reassigned. The fix: change it to var count = 0, because this value genuinely changes.
Mistake #3: Forgetting integer division. The wrong code: val average = (7 + 8) / 2 expecting 7.5. The result: 7. The fix: make one operand a decimal: (7 + 8) / 2.0.
Mistake #4: Mixing number types. The wrong code: val total: Int = 5 * 2.5. The error: type mismatch, because Int * Double is a Double. The fix: declare it as Double, or convert on purpose with .toInt() (and accept that the decimals are cut off).
Mistake #5: Single quotes for text. The wrong code: val city = 'Ibadan'. The fix: ' is only for one Char. Use "Ibadan".
Mistake #6: Using a variable before it exists. The wrong code: println(total) on the line above val total = 10. The error: unresolved reference. The fix: declare it first. Statements run top to bottom.
Mistake #7: Bad names. The wrong code: val x1 = 1500, val temp2 = 3. The fix: val platePrice = 1500, val numberOfPlates = 3. Good names are free documentation.
Mistake #8: Trusting Double with money. The wrong code: 0.1 + 0.2 == 0.3, which is false. The fix: store money as whole kobo in an Int or Long, and format it only when you display it.
Question 1. What is the difference between val and var, and which should you reach for first?
val is read-only: once it has a value, it cannot be reassigned. var can be reassigned. Reach for val first, and use var only when the value really has to change.
Question 2. What does println(9 / 2) print, and how would you get 4.5?
It prints 4, because both operands are Int, so Kotlin uses integer division. Make one operand a Double: 9 / 2.0 or 9.0 / 2 gives 4.5.
Question 3. Which type does Kotlin infer for val x = 3_000_000_000L, and why is the L there?
Long. The number is larger than the maximum Int value (2,147,483,647). The L suffix tells Kotlin the literal is a Long.
Question 4. What is wrong with val n: Int = 3.7, and what are two ways to fix it?
A Double literal cannot be stored in an Int. Fix it by declaring val n: Double = 3.7, or by converting on purpose with val n: Int = 3.7.toInt() (which gives 3, cutting off the decimals).
Question 5. When would you use const val instead of val?
For a true constant whose value is known at compile time and is a basic type (number, Boolean, Char or String), declared at the top level of a file or inside an object. Examples are const val TAX_RATE_PERCENT = 5 or a key name. A plain val can hold values that are only worked out while the program runs.
Go deeper: official resources
- Android Basics with Compose, Unit 1, Pathway 1: the official pathway containing the variables codelab.
- Kotlin docs: Basic types: every number type, with its size and range.
- Kotlin docs: Basic syntax: variables, types and operators in compact form.
- Kotlin Playground: try every example in the browser.
Project goal: Write a Kotlin program with two small tools: a bill splitter for friends sharing a meal, and a distance and speed calculator. It uses nothing but variables, types, operators and templates, so it is a good check of today's lesson.
Requirements:
- Create a
const val SERVICE_CHARGE_PERCENT = 10. - Bill splitter: store a food bill of
18500(naira, as anInt), add the service charge (calculated with integer arithmetic), then split the total between4friends. Print the total, each person's share, and the leftover naira that cannot be split evenly (use%). - Speed: store a distance of
128.0km and a time of2.0hours (asDouble). Print the speed in km/h. - Convert that speed into metres per second (divide by 3.6).
- Use
valwherever possible andvaronly where a value really changes. Print every result with a template and a clear label.
Stretch goal: Store the speed as a Double, then print it rounded to a whole number using roundToInt() (remember the import).
import kotlin.math.roundToInt
const val SERVICE_CHARGE_PERCENT = 10
fun main() {
// ----- Bill splitter -----
val foodBill = 18500
val serviceCharge = foodBill * SERVICE_CHARGE_PERCENT / 100
val totalBill = foodBill + serviceCharge
val friends = 4
val share = totalBill / friends
val leftover = totalBill % friends
println("Food bill: ₦$foodBill")
println("Service charge: ₦$serviceCharge")
println("Total: ₦$totalBill")
println("Each of $friends pays: ₦$share")
println("Left over: ₦$leftover")
// ----- Speed -----
val distanceKm = 128.0
val timeHours = 2.0
val speedKmh = distanceKm / timeHours
val speedMs = speedKmh / 3.6
println("Speed: $speedKmh km/h")
println("Speed: $speedMs m/s")
println("Rounded: ${speedKmh.roundToInt()} km/h and ${speedMs.roundToInt()} m/s")
}
Reasoning through the output: the service charge is 18500 times 10, divided by 100, which is 1850. The total is 20350. Divided between 4 friends, that is 5087 each (4 times 5087 is 20348) with 2 left over. The speed is 128.0 divided by 2.0, which is 64.0 km/h. Dividing by 3.6 gives about 17.78 m/s. The exact digits printed for the metres per second figure depend on floating-point rounding, so expect a long decimal beginning 17.77777.... The rounded value is 18.
Expected output (the long decimal is shortened here):
Food bill: ₦18500
Service charge: ₦1850
Total: ₦20350
Each of 4 pays: ₦5087
Left over: ₦2
Speed: 64.0 km/h
Speed: 17.77777... m/s
Rounded: 64 km/h and 18 m/s
Open your finished project in Android Studio. Check that the .gitignore file the wizard created is in the project root. It keeps local.properties, build folders and keystores out of Git. Never commit passwords or signing keys.
Choose Git > Create Git Repository… (or VCS > Enable Version Control Integration in older versions), select the project folder, then open Git > Commit, tick every file, and write the message “Add Android lesson 07 project”.
On github.com click New repository, name it android-lesson-07-create-and-use-variables-in-kotlin, set it to Public, leave Add a README unticked, and click Create repository.
Back in Android Studio choose Git > Manage Remotes…, add the repository URL as origin, then choose Git > Push… and push your main branch. Prefer the terminal? Run git remote add origin <your-url> then git push -u origin main.
Add a README.md with a screenshot of your app running and a short description. Employers and clients read READMEs first. Your project is now part of your public portfolio. 🎉
Lesson 07 complete! 🎉
You covered:
- Lesson Introduction
- Prerequisite Concepts
- Conceptual Understanding: Boxes with Labels and Rules
- The Basic Data Types
- Operators
- Converting Between Number Types
- Two Classic Surprises
- Constants with `const val`
- Worked Example: Temperature and Time Converter
- Setting Up the Same Code in Android Studio
- Guided Practice Exercises
- Common Beginner Mistakes (and How to Fix Them)
- Knowledge Check