Properties

A property is a var or val that’s part of a class.

A var property can be reassigned, while a val property can’t. Each instance of a class gets its own storage for properties:

// Properties/Cup.kt class Cup { var percentFull = 0 } fun main(args: Array<String>) { val c1 = Cup() c1.percentFull = 50 val c2 = Cup() c2.percentFull = 100 println(c1.percentFull) println(c2.percentFull) } /* Output: 50 100 */

Defining a var or val inside a class looks just like defining it outside the class. However, the var or val becomes part of that class, and to refer to it, you must specify its object using dot notation as you see in the selections of percentFull.

c1.percentFull and c2.percentFull contain different values. This shows that each object has its own storage.

A member function can refer to a property within its object without using a dot (that is, without qualifying it):

// Properties/Cup2.kt class Cup2 { var percentFull = 0 val max = 100 fun add(increase: Int): Int { percentFull += increase if (percentFull > max) percentFull = max return percentFull } } fun main(args: Array<String>) { val cup = Cup2() cup.add(50) println(cup.percentFull) cup.add(70) println(cup.percentFull) } /* Output: 50 100 */

The add() function tries to add increase to percentFull but ensures that it doesn’t go past 100%. The member function add(), like the property percentFull, is defined inside the class Cup2. To refer to either a property or member function from outside a class, you use a dot between the object and the name of the property or member function.

You can declare a property at the top level:

// Properties/TopLevelProperty.kt val constant = 42 var counter = 0 fun inc() { counter++ }

While declaring a constant (val) at the top level is a perfectly fine thing to do, declaring a mutable (var) top-level property is considered an anti-pattern. As your program becomes more complicated, it becomes harder to reason correctly about shared mutable state. If everyone in your code base can access the var counter (meaning counter is shared), you can’t guarantee that it’s always changed in a correct way: while inc() increases it by one every time, some other part of the program might erroneously decrease it by ten, producing obscure bugs. It’s better to hide this state inside a class. In Constraining Visibility you’ll see how to make it truly hidden.

To say that vars can be changed while vals cannot is an oversimplification. As an analogy, consider a house as a val, and a sofa inside the house as a var. You can change the sofa inside the house because it’s a var. You can’t reassign house, though, because it’s a val:

// Properties/ChangingAVal.kt class House { var sofa: String = "" } fun main(args: Array<String>) { val house = House() house.sofa = "Simple sleeper sofa: $89.00" println(house.sofa) house.sofa = "New leather sofa: $3,099.00" println(house.sofa) // Cannot reassign the val to a new House: // house = House() } /* Output: Simple sleeper sofa: $89.00 New leather sofa: $3,099.00 */

Although house is a val, its object can be modified because sofa in class House is a var.

Defining house as a val only prevents it from being reassigned to a new object. If we make a property a val, it cannot be reassigned, as you would expect:

// Properties/AnUnchangingVar.kt class Sofa { val cover: String = "Loveseat cover" } fun main(args: Array<String>) { var sofa = Sofa() // Not allowed: // sofa.cover = "New cover" // Reassigning a var: sofa = Sofa() }

Even though sofa is a var, its object cannot be modified because cover in class Sofa is a val. However, sofa can be reassigned to a new object.

We’ve talked about identifiers like house and sofa as if they were objects. However, they actually just refer to objects—house and sofa are called references. One way to see this is to observe that two identifiers can reference the same object:

// Properties/References.kt class Kitchen { var table: String = "Round table" } fun main(args: Array<String>) { val kitchen1 = Kitchen() val kitchen2 = kitchen1 println("kitchen1: ${kitchen1.table}") println("kitchen2: ${kitchen2.table}") kitchen1.table = "Square table" println("kitchen1: ${kitchen1.table}") println("kitchen2: ${kitchen2.table}") } /* Output: kitchen1: Round table kitchen2: Round table kitchen1: Square table kitchen2: Square table */

When kitchen1 modifies table, kitchen2 sees the modification. Displaying kitchen1.table and kitchen2.table produces the same output.

Remember that var and val control references rather than objects. A var allows you to rebind a reference to a different object, and a val prevents you from doing so.

Mutability means an object can change its state. In the examples above, class House and class Kitchen produce mutable objects while class Sofa produces immutable objects.

Previous          Next

©2018 Mindview LLC. All Rights Reserved.