Classes and objects
Real apps deal in things — a player, a note, a timer session — not loose values. A class is the blueprint for one kind of thing, describing what it knows and what it can do.
The moment loose values stop working
Here is a two-player game, written with everything you know so far:
1var name1 = "Ada"
2var score1 = 0
3var name2 = "Alan"
4var score2 = 0It works. Now the game grows to four players. Then you want to record how many turns each person has had. Then whether they are still in the round.
Count what you are now juggling: sixteen separate names, four of which you must remember to update together every single time anybody rolls. Miss one and the scores drift apart quietly, which is the worst kind of bug.
The real problem is that the language does not know these things belong together. You know name1 and score1 describe one person. Kotlin has no idea.
A is how you tell it.
Think of a blank membership card at a sports club.
The blank card is printed once. It has a box for a name, a box for a membership number, and a box for points. The blank does not belong to anybody — it just says what a member card consists of.
Then the club prints a hundred of them and fills them in. Each filled-in card is a real member with their own name and their own points. Scribbling on one card cannot change another.
The blank is the class. Each filled-in card is an — one real thing made from the blueprint.
Your first class
1class Player(val name: String, var score: Int = 0)
2
3fun main() {
4 val ada = Player("Ada")
5 val alan = Player("Alan", 5)
6 println(ada.name)
7 println(alan.score)
8}That prints Ada then 5. One line of class declaration replaced all four loose values, and it will still be one line when the game has forty players.
Three things to notice.
The brackets after the class name are the . They say what you must supply in order to make one. Player needs a name; the score is optional because it has a default of zero, exactly like the default from Lesson 1.9.
val name inside the constructor does two jobs at once. It takes the name in and keeps it as a of the object. Leave the val off and the value would be used during construction and then thrown away.
There is no new keyword. Player("Ada") is all it takes to build one. If you have seen Java or C#, this is one of the first things Kotlin quietly deletes.
Each thing you build is an . ada and alan are two instances of the same class, and they are completely independent — ada.score and alan.score are different boxes that happen to have the same label.
val and var work exactly as you would expect
1ada.score = 12 // fine, score is a var
2ada.name = "Zoe" // e: Val cannot be reassignedA player's name is fixed for the life of the object; their score is not. You decide that when you write the class, and the holds everyone to it afterwards — including you at midnight in three weeks.
Giving the class something to do
A class is not just a bag of values. It can hold functions too, and inside them it can see its own properties without any dots at all:
1class Player(val name: String, var score: Int = 0) {
2 fun scoreUp(points: Int) {
3 score = score + points
4 }
5}A function that lives inside a class is called a . You call it on an object:
ada.scoreUp(3)Inside scoreUp, the bare word score means this particular player's score. Ada's call changes Ada's score and nobody else's. If you ever need to be explicit about which object you mean, the word refers to it:
this.score = this.score + pointsIdentical meaning, more typing. Kotlin lets you drop this whenever there is no ambiguity, and by convention you do.
Keeping some things to yourself
Not every property should be public. Mark one and only code inside the class can touch it:
1class Player(val name: String) {
2 private var rolls = 0
3}Now ada.rolls will not compile from outside. That is not paranoia — it is a promise. The outside world can only change a Player through the methods you provided, so those methods are the only places a bug can come from.
A useful rule while you are learning: make properties val until you need them to change, and private until something outside genuinely needs to read them. Loosening a restriction later is easy. Tightening one after ten files depend on it is not.
One rough edge
Print an object and you get something disappointing:
println(alan)Player@1b6d3586That is the class name and a machine address. It tells you nothing about Alan, and the digits will be different every time you run the program. Comparing two objects with == is disappointing in the same way — two players with identical names and scores count as different.
Both of those are fixable in one word, and that word is the whole of Lesson 1.15.
- Open Pocket Studio, tap Projects, and open your Kotlin practice project.
- Tap Editor and open
Playground.kt. - Select everything in the file and delete it.
- Type the program from the walkthrough above.
- Tap Run and read the Output panel.
You should see this — except that the digits after the @ will be different on your phone, which is expected:
1Ada: 7 in 2 rolls
2Alan: 5 in 0 rolls
3Player@1b6d3586- Now add
println(ada.rolls)just before the closing brace ofmain, and tap Run. - It refuses to build: Cannot access 'rolls': it is private in 'Player'. That is
privatedoing its job. Delete the line. - Try
alan.name = "Al"instead, and run. It fails too — Val cannot be reassigned — because the constructor declared the name as aval. Delete the line and run once more to be sure the program is back to working.
val, which is set once when the object is built and never again.var in the constructor. If it does not, build a fresh object instead of editing this one.Player() but the constructor requires a name. Kotlin will not invent one, and unlike score it has no default.Player("Ada"). Or give the parameter a default in the class, as val name: String = "Anon", if a nameless player is a real thing in your game.fun rollCount(): Int = rolls — or drop the private if it was never really secret.ada.score() with brackets. score is a property, not a method, so it takes no brackets.ada.score. Properties are read with a bare dot; only methods take round brackets after them.scoreup and scoreUp are two entirely different names, and only one of them exists.ada.scoreUp(3). Pocket Studio's autocomplete will finish the name for you if you type the first few letters and tap the suggestion.- A is a blueprint describing what one kind of thing knows and can do; an is one actual thing built from it.
- The brackets after the class name are the . Writing
valorvarthere turns the input into a you keep. - There is no
newin Kotlin —Player("Ada")builds one. - A is a function inside a class. It sees its own object's properties with no dot; names that object explicitly.
- keeps a property inside the class, so the only way to change it is through methods you wrote.
- Next: data classes — one extra word that gives you readable printing, sensible
==, and a freecopy.