Pocket Studio Academy
HomePart 11.14

Classes and objects

Full course11 min read·4 questions

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:

Playground.ktkotlin
1var name1 = "Ada"
2var score1 = 0
3var name2 = "Alan"
4var score2 = 0

It 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 it like this

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

Playground.ktkotlin
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

Playground.ktkotlin
1ada.score = 12   // fine, score is a var
2ada.name = "Zoe" // e: Val cannot be reassigned

A 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:

Playground.ktkotlin
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:

Playground.ktkotlin
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:

Playground.ktkotlin
this.score = this.score + points

Identical 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:

Playground.ktkotlin
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.

Tip

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:

Playground.ktkotlin
println(alan)
text
Player@1b6d3586

That 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.

Try it in Pocket Studio
  1. Open Pocket Studio, tap Projects, and open your Kotlin practice project.
  2. Tap Editor and open Playground.kt.
  3. Select everything in the file and delete it.
  4. Type the program from the walkthrough above.
  5. 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:

text
1Ada: 7 in 2 rolls
2Alan: 5 in 0 rolls
3Player@1b6d3586
  1. Now add println(ada.rolls) just before the closing brace of main, and tap Run.
  2. It refuses to build: Cannot access 'rolls': it is private in 'Player'. That is private doing its job. Delete the line.
  3. Try alan.name = "Al" instead, and run. It fails too — Val cannot be reassigned — because the constructor declared the name as a val. Delete the line and run once more to be sure the program is back to working.
Error Doctor5 common errors
e: Val cannot be reassigned
MeansYou assigned to a property that was declared with val, which is set once when the object is built and never again.
FixIf that value genuinely needs to change over the object's life, declare it as var in the constructor. If it does not, build a fresh object instead of editing this one.
e: No value passed for parameter 'name'
MeansYou wrote Player() but the constructor requires a name. Kotlin will not invent one, and unlike score it has no default.
FixPass it: 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.
e: Cannot access 'rolls': it is private in 'Player'
MeansYou tried to read or write a private property from outside the class. Private means the class keeps it to itself.
FixEither add a method inside the class that exposes what the outside actually needs — fun rollCount(): Int = rolls — or drop the private if it was never really secret.
e: Expression 'score' cannot be invoked as a function. The function 'invoke()' is not found
MeansYou wrote ada.score() with brackets. score is a property, not a method, so it takes no brackets.
FixWrite ada.score. Properties are read with a bare dot; only methods take round brackets after them.
e: Unresolved reference: scoreup
MeansKotlin is case-sensitive, so scoreup and scoreUp are two entirely different names, and only one of them exists.
FixMatch the spelling in the class exactly: ada.scoreUp(3). Pocket Studio's autocomplete will finish the name for you if you type the first few letters and tap the suggestion.
Recap
  • 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 val or var there turns the input into a you keep.
  • There is no new in 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 free copy.