Lesson 3 Classes and objects (1)
Lesson 3 Classes and objects (1)
in Kotlin including classes and inheritance. You may already know about object
oriented programming from other languages, so some of these concepts may be
familiar to you. We’ll also cover unique aspects of the Kotlin language like extension
functions and special classes, which make it easier for you to write your code as a
developer.
Classes are blueprints for objects. Each class can contain properties and methods to
operate on the object. You can have different classes for different types of objects. In
this example, we have a class (or blueprint) for a House. From the blueprint, we can
create actual House object instances. Each House object instance has all the fields
and methods listed in the definition of the class.
The House class also contains methods like updating the house color or putting the
house on sale.
We use the class to create object instances of the class. On the right are 3 different
House object instances that have different attributes. They have different colors and
one is even for sale.
Transition: 1 click
Let’s look at the code for how to define and use a class. To define a class, use the
keyword class followed by the name of the class, which is House in this case. Then
use curly braces around the class body. Inside this class definition, we see 3
properties and 1 function.
Use the syntax on the right to create a new object instance of the House class. Use
the class name followed by parentheses. In other languages, you might use the “new”
keyword to create new object instances, but in Kotlin there is no “new” keyword.
Another reason why Kotlin is a more concise language!
Resource:
● Classes and Inheritance
Transition: 1 click
A class declaration consists of the class name, the class header (specifying its type
parameters, the primary constructor, etc.) and the class body, surrounded by curly
braces. Both the header and the body are optional; if the class has no body, curly
braces can be omitted, as shown in the example above..
A class in Kotlin can have a primary constructor and one or more secondary
constructors. The primary constructor is part of the class header: it appears after the
class name (and optional type parameters).
These are different examples of how you can define constructors with or without
parameters.
The constructor for class B has 1 input parameter: x which is an Int. Because the
parameter is not marked as a var or val, the variable x does not exist outside the
scope of the constructor. Hence, if we create an object instance called bb, and we try
to call the property x on it, we will get a compiler error. The property x does not exist
on the object.
In the third case, we have a constructor for class C with 1 input parameter: a val
called y. If you create an object instance called cc, you can access the property y
which has the value of 42 in this case.
To summarize, you can define the properties directly within the constructor, using var
or val (as seen in the third example).
Transition: 1 click
That means, parameters can have default values and don’t need to be specified when
the object is initialized. If no default value is provided, then the parameter is required
in the constructor. You can mix default and required parameters in a constructor.
For this class declaration of Box, width and height have default values. No default
value is provided for length, so specifying the length is required when creating a
new Box object. Because these three parameters are marked with val, we know
these are member variables of the Box class.
Underneath the class declaration, we have three examples of how you would create
new Box objects. Even though they specify the constructor arguments in different
ways, the three Box objects are equivalent.
Transition: 1 click
For all examples we’ve seen so far, the constructor is within the class header. This is
called the primary constructor.
This syntax makes Kotlin more concise when it comes to defining classes.
Technically, the first code snippet is equivalent to the second one (which is more
verbose). The second code snippet looks more like how you would define a class in
another language, where the constructor is explicitly defined by itself.
But in Kotlin, write your code according to the first code snippet. Notice the init block?
Let's take a look at that.
In Kotlin, you can’t put any code inside a primary constructor, so put any initialization
code in an init block - also known as an initializer block. You can have more
than 1 init block in your class definition. The blocks just get executed in the order
that they appear in the code. These init blocks essentially become the body of the
primary constructor.
In this example, we have a Square class where the primary constructor has 1 input
parameter: the side length as an Int. We want to do some work in the constructor of
the Square class, so we setup an init block. Within it, we have a println statement.
Outside of the class definition, we can create a new Square object instance with a
side of 10. As soon as it is initialized, the init block is executed and the print
statement gets printed to the output.
What if we need multiple constructors? In addition to the primary constructor, a class
can have one or more secondary constructors. Use the constructor keyword to
declare them.
A secondary constructor must call the primary constructor using the this keyword.
Or it must call another secondary constructor that calls the primary constructor.
Warning: Using multiple constructors leads to more code paths and issues with
testing. Before writing a secondary constructor, consider whether a factory function
would work instead, to keep the class definition clean.
Resource:
● Classes and Inheritance
In the Circle class, we have a primary constructor (that takes a double radius value
as input) and 2 secondary constructors.
This is what the general format would look like for declaring a mutable property in a
class. Declare it with var and then the property name. Use a colon followed by the
data type. Then set it equal to an initial value (this can be an expression). Then you
can optionally override the get() or set() function for the property.
Resource:
● Properties
Let’s look at an example in the Person class. Define a variable called fullName,
with a custom getter function that combines the first and last name into a single
String. You can access fullName like a property (using person.fullName in the
example), and the get() function gets executed on the fly.
For that same property, you can also define a custom setter function that takes in a
single string and splits it into first name and last name components. You can set
fullName like a property, using person.fullName = <the new name>, which
is “Jane Smith” in the example. The underlying set() function would get called.
In addition to constructors and properties, classes can also have member functions.
Declare functions according to the rules we discussed in "Functions" in Lesson 2.
Resource:
● Member Functions
Classes are very useful, and you can receive even more benefit from them by
inheriting properties and behavior from other classes. Let’s talk about inheritance in
Kotlin next.
Inheriting the properties and capabilities from parent class to child class is called
Inheritance.
Kotlin has a single-parent class inheritance structure, should you need multiple class
features to be inherited in your class, consider using Interfaces.
Resource:
● Inheritance
Transition: 1 click
This is how you would create a new Circle object instance, call its
computeArea() method, and prints the result to the output.
In Kotlin, classes are final by default, meaning that they cannot be inherited. To make
a class inheritable, you need to use the open keyword, as explained on the next
slide.
Resource:
● Inheritance
By using the open keyword to declare class C, we can successfully define class D as
a subclass of C.
Because superclasses need to have one of their constructors used, even a no-arg
one, you can easily determine which are classes and which are interfaces by the lack
of parentheses.
Abstract classes do not need to be marked with the open keyword because it’s
implied that they will be subclassed.
Abstract classes share some commonality with interfaces: that is, you can’t directly
instantiate an abstract class, and there can be functions that you must override in
your subclasses. They differ from interfaces in that abstract classes can provide
implementations of functions and properties. For interfaces, all declared properties
are by default abstract and must be provided by the subclasses. In our Pizza
example, we inherited the consume() function, but had to override the properties
kcal and name.
Resource:
● Abstract classes
● Classes and Inheritance
Resource:
● Data Classes
The data class version of Player displays the member variables without needing to
implement a toString() method explicitly.
Resource:
● Collections
Note that the first, second, and third values can be of different types, as shown in the
example for bookAuthorYear.
Transition: 1 click
In other languages, you’d use some for of getInstance and making things
private.
Members of companion objects may look like static members from other languages,
but they are part of real object instances. For example, you can use companion
objects when defining constants in a class, when you want it closely coupled to the
class, and when you only need one instance of it.
Resource:
● Companion Objects
Note: Companion objects can implement an interface and are real objects.
Resource:
● Companion Objects
The defining line of when you should split into a new file is somewhat a matter of
personal preference.
In program source code, packages are used to group related program elements such
as classes, variables, and functions. In Kotlin, these elements are declared in files
with a package declaration at the top of the file. To use elements that are part of a
package in another package, you import the package. The package name customarily
contains only lowercase letters (no underscores) and separating dots and has to be
globally unique. For example: package org.example.game.
At time of writing, package private has not been implemented in Kotlin, so the
package doesn’t limit visibility as it would in other languages.
If the Moped class and its subclasses aren’t too long, it’s fine to put them in the same
class. Same with the Car and its descendants.
Classes, objects, interfaces, constructors, functions, properties, and their setters can
have visibility modifiers
In other languages, you have to explicitly specify “public”. But in Kotlin, if you don’t
specify a visibility modifier, it is public by default.
Resource:
● Visibility Modifiers