**Classes** # Objects - An _object_ models a real-world entity that has both _state_ and _behavior_. Every object belongs to some _type_(s). - A real world example: snoopy is an object of type dog. Its state is determined by several things including its color; and its behavior includes barking. - In Kotlin's parlance, the object's state is made up of a set of _properties_, and its behaviours are called _member functions_. - We model a property by either a variable or function; and model a member function by a function. - Kotlin uses the `.` operator to access an object's property or member function. Thus, `o.p` denotes the property `p` of object `o`, and `o.f()` calls the member function `f()` of object `o`. - E.g., if we were to write Kotlin code that has a `Dog` named `snoopy`, we would refer to snoopy's color as `snoopy.color` and make it bark by calling `snoopy.bark()`. - Objects are created during runtime in a special part of the JVM memory called the _heap_. - New objects must be created explicitly by the programmer but you don't have to worry about destroying objects. Kotlin has _automatic garbage collection_---part of the runtime system that periodically sweeps through the heap to reclaim memory currently allocated to objects but are no longer in active use. # Object instantiation - Every object has a type. (In fact, it can have several types.) A special type of an object is its `class`. An object is an `instance` of its class. Creating a new object is called `instantiation`. - Strings and objects of primitive types can be instantiated at the source code level simply by writing their literals, like `2`, `1.414`, `"Hello"`, etc. Objects of other types can be instantiated by calling a special function called `constructor`. A constructor is called using the name of the class itself. - A class can have more than one constructor, with each constructor behaving differently from the others, depending on the arguments provided at call time. - E.g. to create an empty `StringBuilder` object, we can write ```kotlin val sb = StringBuilder() ``` but if we want to create a `StringBuilder` object and also initialize its content to `"Initial"`, we can write ```kotlin val sb = StringBuilder("Initial") ``` # Defining classes - A `class definition` is a template describing what an object of that particular class is like. A class is defined by specifying its name, constructors, properties, and member functions. - The simplest class is an empty class. It's defined in either of these ways: ```kotlin class Empty1 {} class Empty2 ``` - If you did not define any constructor, Kotlin automatically provides a special constructor called the `default constructor`. The default constructor takes no parameters. Its job is to create a new object of that class and intialize all its properties with default values. - E.g., after the above definition of `Empty1` and `Empty2` classes we can create new objects of those types by writing ```kotlin val e1 = Empty1() val e2 = Empty2() ``` Of course you can't do much else with an empty class! Note that the pair of parentheses is needed when instantiating objects, just like it's needed when calling a function. # Properties - A class property is defined using the `val` or `var` keyword just like local variables in function definition. A `val` property must be assigned a value exactly once, when the object is newly created, but it can be read multiple times. A `var` property can be read or reassigned a new value multiple times. - E.g., we can declare a `Person` class like this: ```kotlin class Person { var firstName: String = "Samuel" var lastName: String = "Adams" var age: Int = 301 } ``` - Note that every non-abstract property must be initialized somewhere within the class definition. The Kotlin compiler will be unhappy if you fail to properly initialize properties and will refuse to compile your program. - Once declared, we can do ```kotlin fun main() { val person = Person() println("${person.firstName} ${person.lastName}, ${person.age}") person.firstName = "Benjamin" person.lastName = "Franklin" person.age = 317 println("${person.firstName} ${person.lastName}, ${person.age}") } ``` - The program will print ``` Samuel Adams, 301 Benjamin Franklin, 317 ``` - It is of course a pain in the neck having to initialize the properties of every new object this way---by changing their values from the default after the object has already been created. It would be a lot nicer if we can initialize their values right at object instantiation time. We'll learn in the next few slides that this is indeed possible. - Kotlin by default implements a property by having 1. a backing `field` to keep its value, 1. a `get` function to access this value, and also 1. a `set` function if the property is a `var` property. - When the programmer writes `person.firstName`, Kotlin actually accesses the backing field of `firstName` through its `get` function; and when she writes `person.firstName = "Benjamin"`, Kotlin actually assigns "Benjamin" to the the backing field of `firstName` through its `set` function. - We can override Kotlin's default behavior on properties by providing our own `get` function, e.g. ```kotlin class Rectangle { val height: Int = 10 val width: Int = 20 val isSquare: Boolean get() { return height == width } } ``` - The property `isSquare` does not have a backing field. Its value is computed anew everytime the programmer accesses `isSquare`. So `isSquare` behaves exactly like a member function. - Sometimes it can be hard to decide whether something should be written as a property or a member function. In such a case, use your own judgement. - Note that we do not have to declare that the `get` function of `isSquare` returns `Boolean` since the compiler can infer that from the declared type of `isSquare`. - It is customary for the name of a `Boolean` property to have the "is" prefix. In fact, you should name all your `Boolean` property like that if your Kotlin code needs to interoperate with Java code. The Kotlin compiler relies on such a convention. - We can also override Kotlin's default `set` function with our own, e.g. ```kotlin class Person { var firstName: String = "Samuel" var lastName: String = "Adams" var age: Int = 96 set(value) { if (value !in 0..150) throw Exception("$value is out of range") else field = value } } ``` - Note that we do not have to declare the type of `value` since the compiler can infer that from the declared type of the property. - A keyword in this context is `field`. It's the name of the backing field for the property. You don't have to declare it or its type. The compiler already knows what `field` means and what its type is! # Primary constructors - Recall that a constructor is a special member function that when called will create a new instance of that class. There are two kinds of constructors: _primary_ and _secondary_. A class can have zero or one primary constructor, and zero or more secondary constructors. - The primary constructor, if exists, is declared in the class header right after the class name, by using the word `constructor` followed by a comma-separated list of "parameter: type" declarations and enclosed in a pair of parentheses, like this: ```kotlin class Person constructor(_firstName: String, _lastName: String, _age: Int) { ... } ``` - In fact, when the word `constructor` is not preceded by annotations or visibility modifiers, like in the declaration above, it can be safely dropped. So the above declaration is equivalent to the shorter ```kotlin class Person(_firstName: String, _lastName: String, _age: Int) { ... } ``` - Initial values used to initialize the properties of a new class instance are usually fed to the constructor through its parameters. However, the primary constructor has no body, so Kotlin provides _initializer blocks_ for initialization. Whenever the primary constructor is called, the code in all initializer blocks are executed in the order they are written in the class definition. E.g., the `Person` class with a primary constructor can be written like this ```kotlin class Person(_firstName: String, _lastName: String, _age: Int) { var firstName: String var lastName: String var age: Int init { firstName = _firstName lastName = _lastName age = _age } } ``` - The above code pattern occurs so often that Kotlin provides this shorthand notation for writing it: ```kotlin class Person(var firstName: String, var lastName: String, var age: Int) ``` - Of course, parameters of the primary constructor can have default values like this: ```kotlin class Person( var firstName: String, var lastName: String = "UNKNOWN", var age: Int = 0 ) { ... } ``` - It's posssible for a class to not have a primary constructor. In that case, every property must have a default value, or have its value initialized in some initializer block. # Secondary constructors - A secondary constructor is also declared with the word `constructor`. It can take some parameters just like the primary constructor. It differs from the primary constructor in these ways: + It is declared within the class body. + It can have a code body just like a normal function definition. + If the class has a primary constructor, the secondary constructor must delegate to the primary constructor (or to other secondary constructor which must ultimately delegate to the primary constructor) before executing the code in its body. The delegation to the other constructor is done via the `this` keyword. - It's possible that a class has no primary constructor yet has a secondary constructor. In that case, the secondary constructor does not have to delegate to the primary constructor, but code in all initializer blocks are executed anyway, implicitly. - Here is an example class definition with a secondary constructor definition. ```kotlin class Person(var firstName: String, var lastName: String, var age: Int) { var middleName: String = "" constructor(firstName: String, middleName: String): this(firstName, "Unknown", 0) { this.middleName = middleName } } ``` - Note that the keyword `this` is used at two different places. The first use in `this(firstName, "Unknown", 0)` is a call to the primary constructor. The second use in `this.middleName = middleName` references "this object"---the runtime class instance that is executing this code. We need it here in order to reference the `middleName` property. Writing `middleName` instead of `this.middleName` would reference the secondary constructor's second parameter instead! - Once we have written the preceding class definition, we can create a new `Person` in two ways: either by calling the primary constructor or calling the secondary constructor. - The compiler determines which constructor is being called by matching the arguments of the call with the constructors' signatures. E.g., if one writes ```kotlin val person1 = Person("John", "Doe", 20) ``` then the primary constructor will be invoked, but if one writes ```kotlin val person2 = Person("William", "Henry") ``` then the secondary constructor will be invoked. - This implies that **no two constructors can have the same signature**. # Member functions - A member function is defined using exactly the same syntax as any top-level function. It differs in only one respect: its definition is written inside the class body instead of at the top-level. - Here is an example class definition that declares two `var` properties, and defines one primary constructor and two member functions. ```kotlin class Point(var x: Double = 0.0, var y: Double = 0.0) { fun translate(xAmount: Double, yAmount: Double) { x += xAmount y += yAmount } fun show() { StdDraw.point(x, y) } } ``` # Using classes - This is an example of how we can use the Point class. ```kotlin import kotlin.random.* import java.awt.Color import StdDraw.* fun main() { setScale(-11.0, 11.0) setPenColor(BLUE) setPenRadius(0.01) val pt = Point() pt.show() repeat(100) { var dx = Random.nextDouble() if (Random.nextInt(2) == 0) dx = -dx var dy = Random.nextDouble() if (Random.nextInt(2) == 0) dy = -dy pt.translate(dx, dy) pt.show() } } ``` _---San Skulrattanakulchai_