Let’s emphasize the difference between defining a custom getter vs. initializing a property directly:
//sampleStart import java.time.Instant class TimeOfStuff { // This represents a readonly property of type Instant, // >with< a backing field, whose value is set during // construction (specifically during execution of the // primary constructor) to the current time. It uses // the default getter. val timeOfConstruction: Instant = Instant.now() // This represents a readonly property of type Instant, // >without< a backing field, whose value is calculated // whenever it is accessed (i.e. whenever the getter is // called). It defines a custom getter val timeOfAccess: Instant get() = Instant.now() } fun main() { val timeOfStuff = TimeOfStuff() val timeOfConstruction1 = timeOfStuff.timeOfConstruction val timeOfAccess1 = timeOfStuff.timeOfAccess Thread.sleep(1000) val timeOfConstruction2 = timeOfStuff.timeOfConstruction val timeOfAccess2 = timeOfStuff.timeOfAccess println(timeOfConstruction1.epochSecond == timeOfConstruction2.epochSecond) println(timeOfAccess1.epochSecond != timeOfAccess2.epochSecond) } //sampleEnd
In the above, also note how Kotlin automatically translates Java properties into Kotlin properties. The actual method defined on the Instant
class is public long getEpochSecond()
. This is done with all Java code called from Kotlin.
While fields and properties are almost exclusively discussed in the context of classes, the definitions at the top are not bound to the concept of a class. Indeed, Kotlin also permits properties defined at the top-level, outside of classes:
//sampleStart import java.time.Instant val formattedTimestamp = "The time is:" get() = "$field ${Instant.now()}" class Test { fun getNow() = formattedTimestamp } //sampleEnd fun main() { val poem = """ Kotlin, the architect of code's ballet, With sealed classes, it pirouettes away. In the world of programming, a dance so fine, With Kotlin, your code will shine! """.trimIndent() println(poem) }
However, it is not possible to define properties with custom getters/setters inside functions. When used inside functions, val
and var
declare variables, which contain some value, and not properties (a pair of functions).
Exercises
Now that we know how properties behave, we are ready to write a mutable version of Person
from a previous lesson.
Rewrite this class in Kotlin:
//sampleStart public class Person { private String title; private String firstName; private String middleName; private String lastName; public Person( String title, String firstName, String middleName, String lastName ) { this.title = title; this.firstName = firstName; this.middleName = middleName; this.lastName = lastName; } public Person(String firstName, String lastName) { this("", firstName, "", lastName); } public String getTitle() { return title; } public void setTitle(final String newTitle) { title = newTitle; } public String getFirstName() { return firstName; } public void setFirstName(final String newFirstName) { firstName = newFirstName; } public String getMiddleName() { return middleName; } public void setMiddleName(final String newMiddleName) { middleName = newMiddleName; } public String getLastName() { return lastName; } public void setLastName(final String newLastName) { lastName = newLastName; } public static class Builder { private String title = ""; private String firstName = ""; private String middleName = ""; private String lastName = ""; public void setTitle(final String newTitle) { title = newTitle; } public void setFirstName(final String newFirstName) { firstName = newFirstName; } public void setMiddleName(final String newMiddleName) { middleName = newMiddleName; } public void setLastName(final String newLastName) { lastName = newLastName; } public Person buildPerson() { return new Person( title, firstName, middleName, lastName ); } } } //sampleEnd
import org.junit.Assert import org.junit.Test class Test { @Test fun testPerson() { PersonKotlin( firstName = "Thomas", middleName = "Michael", lastName = "Shelby" ).apply { Assert.assertTrue("Firstname is not implemented correctly", firstName == "Thomas") Assert.assertTrue("MiddleName is not implemented correctly", middleName == "Michael") Assert.assertTrue("LastName is not implemented correctly", lastName == "Shelby") } PersonKotlin( title = "Mr.", lastName = "Bean" ).apply { Assert.assertTrue("Title is not implemented correctly", title == "Mr.") } PersonKotlin().apply { title = "Mrs." firstName = "Jane" middleName = "Jen" lastName = "Doe" Assert.assertTrue("Title is not mutable", title == "Mrs.") Assert.assertTrue("Firstname is not mutable", firstName == "Jane") Assert.assertTrue("MiddleName is not mutable", middleName == "Jen") Assert.assertTrue("LastName is not mutable", lastName == "Doe") } } } //sampleStart /** * Adapt the essence of the Person.java class to the Kotlin world. */ class PersonKotlin //sampleEnd