[go: up one dir, main page]

0% found this document useful (0 votes)
97 views156 pages

Bpo1 Module 4

Classes in Kotlin can contain constructors, initializer blocks, functions, properties, nested classes, and object declarations. Classes can inherit from other classes using the colon syntax, and methods and properties can be overridden using the override keyword. During construction, the base class initializer is called before the derived class initialization logic. Code in a derived class can call the superclass implementation using the super keyword.

Uploaded by

Ej Molina
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
97 views156 pages

Bpo1 Module 4

Classes in Kotlin can contain constructors, initializer blocks, functions, properties, nested classes, and object declarations. Classes can inherit from other classes using the colon syntax, and methods and properties can be overridden using the override keyword. During construction, the base class initializer is called before the derived class initialization logic. Code in a derived class can call the superclass implementation using the super keyword.

Uploaded by

Ej Molina
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 156

Classes and Inheritance

Classes

Classes in Kotlin are declared using the keyword class:

class Invoice { /*...*/ }

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

class Empty

Constructors

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 goes after the class
name (and optional type parameters).

class Person constructor(firstName: String) { /*...*/ }

If the primary constructor does not have any annotations or visibility modifiers,
the constructor keyword can be omitted:

class Person(firstName: String) { /*...*/ }

The primary constructor cannot contain any code. Initialization code can be placed
in initializer blocks, which are prefixed with the init keyword.

During an instance initialization, the initializer blocks are executed in the same order as
they appear in the class body, interleaved with the property initializers:

class InitOrderDemo(name: String) {


val firstProperty = "First property: $name".also(::println)

init {
println("First initializer block that prints ${name}")
}

val secondProperty = "Second property: ${name.length}".also(::println)


init {
println("Second initializer block that prints ${name.length}")
}
}

Note that parameters of the primary constructor can be used in the initializer blocks.
They can also be used in property initializers declared in the class body:

class Customer(name: String) {


val customerKey = name.toUpperCase()
}

In fact, for declaring properties and initializing them from the primary constructor, Kotlin
has a concise syntax:

class Person(val firstName: String, val lastName: String, var age: Int) { /*...*/ }

You can use a trailing comma when you declare class properties:

class Person(
val firstName: String,
val lastName: String,
var age: Int, // trailing comma
) { /*...*/ }

Much the same way as regular properties, the properties declared in the primary
constructor can be mutable (var) or read-only (val).

If the constructor has annotations or visibility modifiers, the constructor keyword is


required, and the modifiers go before it:

class Customer public @Inject constructor(name: String) { /*...*/ }

Secondary constructors

The class can also declare secondary constructors, which are prefixed
with constructor:

class Person {
var children: MutableList<Person> = mutableListOf()
constructor(parent: Person) {
parent.children.add(this)
}
}
If the class has a primary constructor, each secondary constructor needs to delegate to
the primary constructor, either directly or indirectly through another secondary
constructor(s). Delegation to another constructor of the same class is done using
the this keyword:

class Person(val name: String) {


var children: MutableList<Person> = mutableListOf()
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}

Note that code in initializer blocks effectively becomes part of the primary constructor.
Delegation to the primary constructor happens as the first statement of a secondary
constructor, so the code in all initializer blocks and property initializers is executed
before the secondary constructor body. Even if the class has no primary constructor, the
delegation still happens implicitly, and the initializer blocks are still executed:

class Constructors {
init {
println("Init block")
}

constructor(i: Int) {
println("Constructor")
}
}

If a non-abstract class does not declare any constructors (primary or secondary), it will
have a generated primary constructor with no arguments. The visibility of the
constructor will be public. If you do not want your class to have a public constructor, you
need to declare an empty primary constructor with non-default visibility:

class DontCreateMe private constructor () { /*...*/ }

NOTE: On the JVM, if all of the parameters of the primary constructor have default
values, the compiler will generate an additional parameterless constructor which will use
the default values. This makes it easier to use Kotlin with libraries such as Jackson or
JPA that create class instances through parameterless constructors.
class Customer(val customerName: String = "")
Creating instances of classes

To create an instance of a class, we call the constructor as if it were a regular function:

val invoice = Invoice()

val customer = Customer("Joe Smith")

Note that Kotlin does not have a new keyword.

Class members

Classes can contain:

• Constructors and initializer blocks


• Functions
• Properties
• Nested and Inner Classes
• Object Declarations

Inheritance

All classes in Kotlin have a common superclass Any, that is the default superclass for a
class with no supertypes declared:

class Example // Implicitly inherits from Any

Any has three methods: equals(), hashCode() and toString(). Thus, they are
defined for all Kotlin classes.

By default, Kotlin classes are final: they can’t be inherited. To make a class inheritable,
mark it with the open keyword.

open class Base //Class is open for inheritance

To declare an explicit supertype, place the type after a colon in the class header:

open class Base(p: Int)

class Derived(p: Int) : Base(p)


If the derived class has a primary constructor, the base class can (and must) be
initialized right there, using the parameters of the primary constructor.

If the derived class has no primary constructor, then each secondary constructor has to
initialize the base type using the super keyword, or to delegate to another constructor
which does that. Note that in this case different secondary constructors can call different
constructors of the base type:

class MyView : View {


constructor(ctx: Context) : super(ctx)

constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)


}

Overriding methods

As we mentioned before, we stick to making things explicit in Kotlin. So, Kotlin requires
explicit modifiers for overridable members (we call them open) and for overrides:

open class Shape {


open fun draw() { /*...*/ }
fun fill() { /*...*/ }
}

class Circle() : Shape() {


override fun draw() { /*...*/ }
}

The override modifier is required for Circle.draw(). If it were missing, the compiler
would complain. If there is no open modifier on a function, like Shape.fill(), declaring
a method with the same signature in a subclass is illegal, either with override or
without it. The open modifier has no effect when added on members of a final class (i.e..
a class with no open modifier).

A member marked override is itself open, i.e. it may be overridden in subclasses. If


you want to prohibit re-overriding, use final:

open class Rectangle() : Shape() {


final override fun draw() { /*...*/ }
}
Overriding properties

Overriding properties works in a similar way to overriding methods; properties declared


on a superclass that are then redeclared on a derived class must be prefaced
with override, and they must have a compatible type. Each declared property can be
overridden by a property with an initializer or by a property with a get method.

open class Shape {


open val vertexCount: Int = 0
}

class Rectangle : Shape() {


override val vertexCount = 4
}

You can also override a val property with a var property, but not vice versa. This is
allowed because a val property essentially declares a get method, and overriding it as
a var additionally declares a set method in the derived class.

Note that you can use the override keyword as part of the property declaration in a
primary constructor.

interface Shape {
val vertexCount: Int
}

class Rectangle(override val vertexCount: Int = 4) : Shape // Always has 4 vertices

class Polygon : Shape {


override var vertexCount: Int = 0 // Can be set to any number later
}

Derived class initialization order

During construction of a new instance of a derived class, the base class initialization is
done as the first step (preceded only by evaluation of the arguments for the base class
constructor) and thus happens before the initialization logic of the derived class is run.

open class Base(val name: String) {

init { println("Initializing Base") }

open val size: Int =


name.length.also { println("Initializing size in Base: $it") }
}

class Derived(
name: String,
val lastName: String,
) : Base(name.capitalize().also { println("Argument for Base: $it") }) {

init { println("Initializing Derived") }

override val size: Int =


(super.size + lastName.length).also { println("Initializing size in Derived: $it") }
}

It means that, by the time of the base class constructor execution, the properties
declared or overridden in the derived class are not yet initialized. If any of those
properties are used in the base class initialization logic (either directly or indirectly,
through another overridden open member implementation), it may lead to incorrect
behavior or a runtime failure. When designing a base class, you should therefore avoid
using open members in the constructors, property initializers, and init blocks.

Calling the superclass implementation

Code in a derived class can call its superclass functions and property accessors
implementations using the super keyword:

open class Rectangle {


open fun draw() { println("Drawing a rectangle") }
val borderColor: String get() = "black"
}

class FilledRectangle : Rectangle() {


override fun draw() {
super.draw()
println("Filling the rectangle")
}

val fillColor: String get() = super.borderColor


}

Inside an inner class, accessing the superclass of the outer class is done with
the super keyword qualified with the outer class name: super@Outer:
class FilledRectangle: Rectangle() {
override fun draw() {
val filler = Filler()
filler.drawAndFill()
}

inner class Filler {


fun fill() { println("Filling") }
fun drawAndFill() {
super@FilledRectangle.draw() // Calls Rectangle's implementation of draw()
fill()
println("Drawn a filled rectangle with color ${super@FilledRectangle.borderColor}")
// Uses Rectangle's implementation of borderColor's get()
}
}
}

Overriding rules

In Kotlin, implementation inheritance is regulated by the following rule: if a class inherits


multiple implementations of the same member from its immediate superclasses, it must
override this member and provide its own implementation (perhaps, using one of the
inherited ones). To denote the supertype from which the inherited implementation is
taken, we use super qualified by the supertype name in angle brackets,
e.g. super<Base>:

open class Rectangle {


open fun draw() { /* ... */ }
}

interface Polygon {
fun draw() { /* ... */ } // interface members are 'open' by default
}

class Square() : Rectangle(), Polygon {


// The compiler requires draw() to be overridden:
override fun draw() {
super<Rectangle>.draw() // call to Rectangle.draw()
super<Polygon>.draw() // call to Polygon.draw()
}
}
It's fine to inherit from both Rectangle and Polygon, but both of them have their
implementations of draw(), so we have to override draw() in Square and provide its
own implementation that eliminates the ambiguity.

Abstract classes

A class and some of its members may be declared abstract. An abstract member
does not have an implementation in its class. Note that we do not need to annotate an
abstract class or function with open – it goes without saying.

We can override a non-abstract open member with an abstract one

open class Polygon {


open fun draw() {}
}

abstract class Rectangle : Polygon() {


abstract override fun draw()
}
Properties

Declaring Properties

Properties in Kotlin classes can be declared either as mutable using the var keyword,
or as read-only using the val keyword.

class Address {
var name: String = "Holmes, Sherlock"
var street: String = "Baker"
var city: String = "London"
var state: String? = null
var zip: String = "123456"
}
To use a property, simply refer to it by name:

fun copyAddress(address: Address): Address {


val result = Address() // there's no 'new' keyword in Kotlin
result.name = address.name // accessors are called
result.street = address.street
// ...
return result
}

Getters and Setters

The full syntax for declaring a property is

var <propertyName>[: <PropertyType>] [= <property_initializer>]


[<getter>]
[<setter>]
The initializer, getter and setter are optional. Property type is optional if it can be
inferred from the initializer.

We can define custom accessors for a property. If we define a custom getter, it will be
called every time we access the property (this allows us to implement a computed
property). Here's an example of a custom getter:

val isEmpty: Boolean


get() = this.size == 0
If we define a custom setter, it will be called every time we assign a value to the
property. A custom setter looks like this:

var stringRepresentation: String


get() = this.toString()
set(value) {
setDataFromString(value) // parses the string and assigns values to other properties
}
By convention, the name of the setter parameter is value, but you can choose a
different name if you prefer.

Interfaces

Interfaces in Kotlin can contain declarations of abstract methods, as well as method


implementations. What makes them different from abstract classes is that interfaces
cannot store state. They can have properties but these need to be abstract or to provide
accessor implementations.

An interface is defined using the keyword interface

interface MyInterface {
fun bar()
fun foo() {
// optional body
}
}
Implementing Interfaces

A class or object can implement one or more interfaces

class Child : MyInterface {


override fun bar() {
// body
}
}

Properties in Interfaces

You can declare properties in interfaces. A property declared in an interface can either
be abstract, or it can provide implementations for accessors. Properties declared in
interfaces can't have backing fields, and therefore accessors declared in interfaces can't
reference them.

interface MyInterface {
val prop: Int // abstract

val propertyWithImplementation: String


get() = "foo"

fun foo() {
print(prop)
}
}

class Child : MyInterface {


override val prop: Int = 29
}

Interfaces Inheritance

An interface can derive from other interfaces and thus both provide implementations for
their members and declare new functions and properties. Quite naturally, classes
implementing such an interface are only required to define the missing implementations:

interface Named {
val name: String
}

interface Person : Named {


val firstName: String
val lastName: String

override val name: String get() = "$firstName $lastName"


}

data class Employee(


// implementing 'name' is not required
override val firstName: String,
override val lastName: String,
val position: Position
) : Person
Object Expressions and Declarations

Sometimes we need to create an object of a slight modification of some class, without


explicitly declaring a new subclass for it. Kotlin handles this case with object
expressions and object declarations.

Object expressions

To create an object of an anonymous class that inherits from some type (or types), we
write:

window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) { /*...*/ }

override fun mouseEntered(e: MouseEvent) { /*...*/ }


})
If a supertype has a constructor, appropriate constructor parameters must be passed to
it. Many supertypes may be specified as a comma-separated list after the colon:

open class A(x: Int) {


public open val y: Int = x
}

interface B { /*...*/ }

val ab: A = object : A(1), B {


override val y = 15
}
If, by any chance, we need "just an object", with no nontrivial supertypes, we can simply
say:

fun foo() {
val adHoc = object {
var x: Int = 0
var y: Int = 0
}
print(adHoc.x + adHoc.y)
}
Note that anonymous objects can be used as types only in local and private
declarations. If you use an anonymous object as a return type of a public function or the
type of a public property, the actual type of that function or property will be the declared
supertype of the anonymous object, or Any if you didn't declare any supertype.
Members added in the anonymous object will not be accessible.

class C {
// Private function, so the return type is the anonymous object type
private fun foo() = object {
val x: String = "x"
}

// Public function, so the return type is Any


fun publicFoo() = object {
val x: String = "x"
}

fun bar() {
val x1 = foo().x // Works
val x2 = publicFoo().x // ERROR: Unresolved reference 'x'
}
}
The code in object expressions can access variables from the enclosing scope.

fun countClicks(window: JComponent) {


var clickCount = 0
var enterCount = 0

window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
clickCount++
}

override fun mouseEntered(e: MouseEvent) {


enterCount++
}
})
// ...
}

Object declarations

Singleton may be useful in several cases, and Kotlin (after Scala) makes it easy to
declare singletons:

object DataProviderManager {
fun registerDataProvider(provider: DataProvider) {
// ...
}

val allDataProviders: Collection<DataProvider>


get() = // ...
}
This is called an object declaration, and it always has a name following
the object keyword. Just like a variable declaration, an object declaration is not an
expression, and cannot be used on the right hand side of an assignment statement.

Object declaration's initialization is thread-safe and done at first access.

To refer to the object, we use its name directly:

DataProviderManager.registerDataProvider(...)

Such objects can have supertypes:

object DefaultListener : MouseAdapter() {


override fun mouseClicked(e: MouseEvent) { ... }

override fun mouseEntered(e: MouseEvent) { ... }


}
NOTE: object declarations can't be local (i.e. be nested directly inside a function), but
they can be nested into other object declarations or non-inner classes.

Companion Objects

An object declaration inside a class can be marked with the companion keyword:

class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
Members of the companion object can be called by using simply the class name as the
qualifier:

val instance = MyClass.create()


The name of the companion object can be omitted, in which case the
name Companion will be used:
class MyClass {
companion object { }
}

val x = MyClass.Companion
The name of a class used by itself (not as a qualifier to another name) acts as a
reference to the companion object of the class (whether named or not):

class MyClass1 {
companion object Named { }
}

val x = MyClass1

class MyClass2 {
companion object { }
}

val y = MyClass2
Note that, even though the members of companion objects look like static members in
other languages, at runtime those are still instance members of real objects, and can,
for example, implement interfaces:

interface Factory<T> {
fun create(): T
}

class MyClass {
companion object : Factory<MyClass> {
override fun create(): MyClass = MyClass()
}
}

val f: Factory<MyClass> = MyClass


However, on the JVM you can have members of companion objects generated as real
static methods and fields, if you use the @JvmStatic annotation.

Semantic difference between object expressions and declarations

There is one important semantic difference between object expressions and object
declarations:
• object expressions are executed (and initialized) immediately, where they
are used;
• object declarations are initialized lazily, when accessed for the first time;
• a companion object is initialized when the corresponding class is loaded
(resolved), matching the semantics of a Java static initializer.
Object Expressions and Declarations

Sometimes we need to create an object of a slight modification of some class, without


explicitly declaring a new subclass for it. Kotlin handles this case with object
expressions and object declarations.

Object expressions

To create an object of an anonymous class that inherits from some type (or types), we
write:

window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) { /*...*/ }

override fun mouseEntered(e: MouseEvent) { /*...*/ }


})
If a supertype has a constructor, appropriate constructor parameters must be passed to
it. Many supertypes may be specified as a comma-separated list after the colon:

open class A(x: Int) {


public open val y: Int = x
}

interface B { /*...*/ }

val ab: A = object : A(1), B {


override val y = 15
}
If, by any chance, we need "just an object", with no nontrivial supertypes, we can simply
say:

fun foo() {
val adHoc = object {
var x: Int = 0
var y: Int = 0
}
print(adHoc.x + adHoc.y)
}
Note that anonymous objects can be used as types only in local and private
declarations. If you use an anonymous object as a return type of a public function or the
type of a public property, the actual type of that function or property will be the declared
supertype of the anonymous object, or Any if you didn't declare any supertype.
Members added in the anonymous object will not be accessible.

class C {
// Private function, so the return type is the anonymous object type
private fun foo() = object {
val x: String = "x"
}

// Public function, so the return type is Any


fun publicFoo() = object {
val x: String = "x"
}

fun bar() {
val x1 = foo().x // Works
val x2 = publicFoo().x // ERROR: Unresolved reference 'x'
}
}
The code in object expressions can access variables from the enclosing scope.

fun countClicks(window: JComponent) {


var clickCount = 0
var enterCount = 0

window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
clickCount++
}

override fun mouseEntered(e: MouseEvent) {


enterCount++
}
})
// ...
}

Object declarations

Singleton may be useful in several cases, and Kotlin (after Scala) makes it easy to
declare singletons:

object DataProviderManager {
fun registerDataProvider(provider: DataProvider) {
// ...
}

val allDataProviders: Collection<DataProvider>


get() = // ...
}
This is called an object declaration, and it always has a name following
the object keyword. Just like a variable declaration, an object declaration is not an
expression, and cannot be used on the right hand side of an assignment statement.

Object declaration's initialization is thread-safe and done at first access.

To refer to the object, we use its name directly:

DataProviderManager.registerDataProvider(...)

Such objects can have supertypes:

object DefaultListener : MouseAdapter() {


override fun mouseClicked(e: MouseEvent) { ... }

override fun mouseEntered(e: MouseEvent) { ... }


}
NOTE: object declarations can't be local (i.e. be nested directly inside a function), but
they can be nested into other object declarations or non-inner classes.

Companion Objects

An object declaration inside a class can be marked with the companion keyword:

class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
Members of the companion object can be called by using simply the class name as the
qualifier:

val instance = MyClass.create()


The name of the companion object can be omitted, in which case the
name Companion will be used:
class MyClass {
companion object { }
}

val x = MyClass.Companion
The name of a class used by itself (not as a qualifier to another name) acts as a
reference to the companion object of the class (whether named or not):

class MyClass1 {
companion object Named { }
}

val x = MyClass1

class MyClass2 {
companion object { }
}

val y = MyClass2
Note that, even though the members of companion objects look like static members in
other languages, at runtime those are still instance members of real objects, and can,
for example, implement interfaces:

interface Factory<T> {
fun create(): T
}

class MyClass {
companion object : Factory<MyClass> {
override fun create(): MyClass = MyClass()
}
}

val f: Factory<MyClass> = MyClass


However, on the JVM you can have members of companion objects generated as real
static methods and fields, if you use the @JvmStatic annotation.

Semantic difference between object expressions and declarations

There is one important semantic difference between object expressions and object
declarations:
• object expressions are executed (and initialized) immediately, where they
are used;
• object declarations are initialized lazily, when accessed for the first time;
• a companion object is initialized when the corresponding class is loaded
(resolved), matching the semantics of a Java static initializer.
MODULE 2
Mobile Application Development 1
Subtopic 2
OBJECTS
• Understand the concept of Objects
• Write program that uses objects
• Know the different applications of objects
To create an object of an anonymous class that inherits
from some type (or types), we write:
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) { /*...*/ }​
override fun mouseEntered(e: MouseEvent) { /*...*/ }
})
If a supertype has a open class A(x: Int) {
constructor, appropriate public open val y: Int = x
constructor parameters }​
must be passed to it.
Many supertypes may interface B { /*...*/ }
be specified as a
val ab: A = object : A(1), B {
comma-separated list
override val y = 15
after the colon: }
If, by any chance, we need "just an object", with no
nontrivial supertypes, we can simply say:
fun foo() {
val adHoc = object {
var x: Int = 0
var y: Int = 0
}
print(adHoc.x + adHoc.y)
}
Anonymous objects can be used as types only in local
and private declarations. If you use an anonymous
object as a return type of a public function or the type of
a public property, the actual type of that function or
property will be the declared supertype of the
anonymous object, or Any if you didn't declare any
supertype. Members added in the anonymous object will
not be accessible. (See next slide for sample code)
class C {
// Private function, so the return type is the anonymous object type
private fun foo() = object {
val x: String = "x“
}
// Public function, so the return type is Any
fun publicFoo() = object {
val x: String = "x“
}
fun bar() {
val x1 = foo().x // Works
val x2 = publicFoo().x // ERROR: Unresolved reference 'x’
}
}
The code in fun countClicks(window: JComponent) {
var clickCount = 0
object var enterCount = 0
expressions window.addMouseListener(object : MouseAdapter() {
can access override fun mouseClicked(e: MouseEvent) {
clickCount++
variables from }
the enclosing override fun mouseEntered(e: MouseEvent) {
enterCount++
scope. }
})
// ...
}
Singleton may
object DataProviderManager {
be useful in fun registerDataProvider(provider: DataProvider) {
several cases, // ...
and Kotlin }
(after Scala) val allDataProviders: Collection<DataProvider>
makes it easy get() = // ...
to declare }
singletons:
What you see in the code in previous slide is called an
object declaration, and it always has a name following
the object keyword. Just like a variable declaration, an
object declaration is not an expression, and cannot be
used on the right hand side of an assignment statement.
An object declaration inside a class can be marked with
the companion keyword:

class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
Members of the companion object can be called by using
simply the class name as the qualifier:
val instance = MyClass.create()
The name of the companion object can be omitted, in
which case the name Companion will be used:
class MyClass {
companion object { }
}​
val x = MyClass.Companion
The name of a class class MyClass1 {
used by itself (not as a companion object Named { }
qualifier to another }​
name) acts as a
reference to the val x = MyClass1​class MyClass2 {
companion object of the companion object { }
class (whether named or }​
not):
val y = MyClass2
Even though the interface Factory<T> {
members of companion fun create(): T
objects look like static }
members in other
languages, at runtime class MyClass {
those are still instance companion object : Factory<MyClass> {
members of real override fun create(): MyClass = MyClass()
objects, and can, for }
example, implement }
interfaces: val f: Factory<MyClass> = MyClass
• Griffiths, D., Griffiths, D. (2019). Head First Kotlin. O'Reilly Media, Inc.
• Jemerov, D., and Isakova, S. (2017). Kotlin in Action. Manning Publications
• Horton, J. (2018). Android Programming for Beginners: Build in-depth, full-featured Android 9 Pie apps starting from
zero programming experience, 2nd Edition. Packt Publishing
• Franceschi, H. J. (2018). Android App Development 1st Edition.
• Iversen, J., Eierman, M. (2018). Mobile App Development for iOS and Android, Edition 2.0. Prospect Press, Inc.
• Phillips, B., Stewart, C., Marsicano, K. (2017). Android Programming: The Big Nerd Ranch Guide (3rd Edition) (Big Nerd
Ranch Guides) 3rd Edition.
• Dawn, G. (2017). Head First Android Development: A Brain-Friendly Guide. O' Reilly Media, Inc.
• Drongelen, M., Dennis, A., Garabedian, R. (2017). Lean Mobile App Development: Apply Lean startup methodologies to
develop successful iOS and Android apps. Packt Publishing
• Miller, C. (2016), Cross-platform Localization for Native Mobile Apps with Xamarin
• Beer, Paula(2015), Hello app inventor: android programming for kids and the rest of us.
• Deborah Gonzalez (2015), Managing Online Risk: Apps, Mobile, And Social Media Security

Online Sources:
https://kotlinlang.org/docs/reference/
http://www.developer.android.com
http://www.android.com
http://www.onefeu.instructure.com
Classes and Inheritance

Classes

Classes in Kotlin are declared using the keyword class:

class Invoice { /*...*/ }

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

class Empty

Constructors

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 goes after the class
name (and optional type parameters).

class Person constructor(firstName: String) { /*...*/ }

If the primary constructor does not have any annotations or visibility modifiers,
the constructor keyword can be omitted:

class Person(firstName: String) { /*...*/ }

The primary constructor cannot contain any code. Initialization code can be placed
in initializer blocks, which are prefixed with the init keyword.

During an instance initialization, the initializer blocks are executed in the same order as
they appear in the class body, interleaved with the property initializers:

class InitOrderDemo(name: String) {


val firstProperty = "First property: $name".also(::println)

init {
println("First initializer block that prints ${name}")
}

val secondProperty = "Second property: ${name.length}".also(::println)


init {
println("Second initializer block that prints ${name.length}")
}
}

Note that parameters of the primary constructor can be used in the initializer blocks.
They can also be used in property initializers declared in the class body:

class Customer(name: String) {


val customerKey = name.toUpperCase()
}

In fact, for declaring properties and initializing them from the primary constructor, Kotlin
has a concise syntax:

class Person(val firstName: String, val lastName: String, var age: Int) { /*...*/ }

You can use a trailing comma when you declare class properties:

class Person(
val firstName: String,
val lastName: String,
var age: Int, // trailing comma
) { /*...*/ }

Much the same way as regular properties, the properties declared in the primary
constructor can be mutable (var) or read-only (val).

If the constructor has annotations or visibility modifiers, the constructor keyword is


required, and the modifiers go before it:

class Customer public @Inject constructor(name: String) { /*...*/ }

Secondary constructors

The class can also declare secondary constructors, which are prefixed
with constructor:

class Person {
var children: MutableList<Person> = mutableListOf()
constructor(parent: Person) {
parent.children.add(this)
}
}
If the class has a primary constructor, each secondary constructor needs to delegate to
the primary constructor, either directly or indirectly through another secondary
constructor(s). Delegation to another constructor of the same class is done using
the this keyword:

class Person(val name: String) {


var children: MutableList<Person> = mutableListOf()
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}

Note that code in initializer blocks effectively becomes part of the primary constructor.
Delegation to the primary constructor happens as the first statement of a secondary
constructor, so the code in all initializer blocks and property initializers is executed
before the secondary constructor body. Even if the class has no primary constructor, the
delegation still happens implicitly, and the initializer blocks are still executed:

class Constructors {
init {
println("Init block")
}

constructor(i: Int) {
println("Constructor")
}
}

If a non-abstract class does not declare any constructors (primary or secondary), it will
have a generated primary constructor with no arguments. The visibility of the
constructor will be public. If you do not want your class to have a public constructor, you
need to declare an empty primary constructor with non-default visibility:

class DontCreateMe private constructor () { /*...*/ }

NOTE: On the JVM, if all of the parameters of the primary constructor have default
values, the compiler will generate an additional parameterless constructor which will use
the default values. This makes it easier to use Kotlin with libraries such as Jackson or
JPA that create class instances through parameterless constructors.
class Customer(val customerName: String = "")
Creating instances of classes

To create an instance of a class, we call the constructor as if it were a regular function:

val invoice = Invoice()

val customer = Customer("Joe Smith")

Note that Kotlin does not have a new keyword.

Class members

Classes can contain:

• Constructors and initializer blocks


• Functions
• Properties
• Nested and Inner Classes
• Object Declarations

Inheritance

All classes in Kotlin have a common superclass Any, that is the default superclass for a
class with no supertypes declared:

class Example // Implicitly inherits from Any

Any has three methods: equals(), hashCode() and toString(). Thus, they are
defined for all Kotlin classes.

By default, Kotlin classes are final: they can’t be inherited. To make a class inheritable,
mark it with the open keyword.

open class Base //Class is open for inheritance

To declare an explicit supertype, place the type after a colon in the class header:

open class Base(p: Int)

class Derived(p: Int) : Base(p)


If the derived class has a primary constructor, the base class can (and must) be
initialized right there, using the parameters of the primary constructor.

If the derived class has no primary constructor, then each secondary constructor has to
initialize the base type using the super keyword, or to delegate to another constructor
which does that. Note that in this case different secondary constructors can call different
constructors of the base type:

class MyView : View {


constructor(ctx: Context) : super(ctx)

constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)


}

Overriding methods

As we mentioned before, we stick to making things explicit in Kotlin. So, Kotlin requires
explicit modifiers for overridable members (we call them open) and for overrides:

open class Shape {


open fun draw() { /*...*/ }
fun fill() { /*...*/ }
}

class Circle() : Shape() {


override fun draw() { /*...*/ }
}

The override modifier is required for Circle.draw(). If it were missing, the compiler
would complain. If there is no open modifier on a function, like Shape.fill(), declaring
a method with the same signature in a subclass is illegal, either with override or
without it. The open modifier has no effect when added on members of a final class (i.e..
a class with no open modifier).

A member marked override is itself open, i.e. it may be overridden in subclasses. If


you want to prohibit re-overriding, use final:

open class Rectangle() : Shape() {


final override fun draw() { /*...*/ }
}
Overriding properties

Overriding properties works in a similar way to overriding methods; properties declared


on a superclass that are then redeclared on a derived class must be prefaced
with override, and they must have a compatible type. Each declared property can be
overridden by a property with an initializer or by a property with a get method.

open class Shape {


open val vertexCount: Int = 0
}

class Rectangle : Shape() {


override val vertexCount = 4
}

You can also override a val property with a var property, but not vice versa. This is
allowed because a val property essentially declares a get method, and overriding it as
a var additionally declares a set method in the derived class.

Note that you can use the override keyword as part of the property declaration in a
primary constructor.

interface Shape {
val vertexCount: Int
}

class Rectangle(override val vertexCount: Int = 4) : Shape // Always has 4 vertices

class Polygon : Shape {


override var vertexCount: Int = 0 // Can be set to any number later
}

Derived class initialization order

During construction of a new instance of a derived class, the base class initialization is
done as the first step (preceded only by evaluation of the arguments for the base class
constructor) and thus happens before the initialization logic of the derived class is run.

open class Base(val name: String) {

init { println("Initializing Base") }

open val size: Int =


name.length.also { println("Initializing size in Base: $it") }
}

class Derived(
name: String,
val lastName: String,
) : Base(name.capitalize().also { println("Argument for Base: $it") }) {

init { println("Initializing Derived") }

override val size: Int =


(super.size + lastName.length).also { println("Initializing size in Derived: $it") }
}

It means that, by the time of the base class constructor execution, the properties
declared or overridden in the derived class are not yet initialized. If any of those
properties are used in the base class initialization logic (either directly or indirectly,
through another overridden open member implementation), it may lead to incorrect
behavior or a runtime failure. When designing a base class, you should therefore avoid
using open members in the constructors, property initializers, and init blocks.

Calling the superclass implementation

Code in a derived class can call its superclass functions and property accessors
implementations using the super keyword:

open class Rectangle {


open fun draw() { println("Drawing a rectangle") }
val borderColor: String get() = "black"
}

class FilledRectangle : Rectangle() {


override fun draw() {
super.draw()
println("Filling the rectangle")
}

val fillColor: String get() = super.borderColor


}

Inside an inner class, accessing the superclass of the outer class is done with
the super keyword qualified with the outer class name: super@Outer:
class FilledRectangle: Rectangle() {
override fun draw() {
val filler = Filler()
filler.drawAndFill()
}

inner class Filler {


fun fill() { println("Filling") }
fun drawAndFill() {
super@FilledRectangle.draw() // Calls Rectangle's implementation of draw()
fill()
println("Drawn a filled rectangle with color ${super@FilledRectangle.borderColor}")
// Uses Rectangle's implementation of borderColor's get()
}
}
}

Overriding rules

In Kotlin, implementation inheritance is regulated by the following rule: if a class inherits


multiple implementations of the same member from its immediate superclasses, it must
override this member and provide its own implementation (perhaps, using one of the
inherited ones). To denote the supertype from which the inherited implementation is
taken, we use super qualified by the supertype name in angle brackets,
e.g. super<Base>:

open class Rectangle {


open fun draw() { /* ... */ }
}

interface Polygon {
fun draw() { /* ... */ } // interface members are 'open' by default
}

class Square() : Rectangle(), Polygon {


// The compiler requires draw() to be overridden:
override fun draw() {
super<Rectangle>.draw() // call to Rectangle.draw()
super<Polygon>.draw() // call to Polygon.draw()
}
}
It's fine to inherit from both Rectangle and Polygon, but both of them have their
implementations of draw(), so we have to override draw() in Square and provide its
own implementation that eliminates the ambiguity.

Abstract classes

A class and some of its members may be declared abstract. An abstract member
does not have an implementation in its class. Note that we do not need to annotate an
abstract class or function with open – it goes without saying.

We can override a non-abstract open member with an abstract one

open class Polygon {


open fun draw() {}
}

abstract class Rectangle : Polygon() {


abstract override fun draw()
}
Properties

Declaring Properties

Properties in Kotlin classes can be declared either as mutable using the var keyword,
or as read-only using the val keyword.

class Address {
var name: String = "Holmes, Sherlock"
var street: String = "Baker"
var city: String = "London"
var state: String? = null
var zip: String = "123456"
}
To use a property, simply refer to it by name:

fun copyAddress(address: Address): Address {


val result = Address() // there's no 'new' keyword in Kotlin
result.name = address.name // accessors are called
result.street = address.street
// ...
return result
}

Getters and Setters

The full syntax for declaring a property is

var <propertyName>[: <PropertyType>] [= <property_initializer>]


[<getter>]
[<setter>]
The initializer, getter and setter are optional. Property type is optional if it can be
inferred from the initializer.

We can define custom accessors for a property. If we define a custom getter, it will be
called every time we access the property (this allows us to implement a computed
property). Here's an example of a custom getter:

val isEmpty: Boolean


get() = this.size == 0
If we define a custom setter, it will be called every time we assign a value to the
property. A custom setter looks like this:

var stringRepresentation: String


get() = this.toString()
set(value) {
setDataFromString(value) // parses the string and assigns values to other properties
}
By convention, the name of the setter parameter is value, but you can choose a
different name if you prefer.

Interfaces

Interfaces in Kotlin can contain declarations of abstract methods, as well as method


implementations. What makes them different from abstract classes is that interfaces
cannot store state. They can have properties but these need to be abstract or to provide
accessor implementations.

An interface is defined using the keyword interface

interface MyInterface {
fun bar()
fun foo() {
// optional body
}
}
Implementing Interfaces

A class or object can implement one or more interfaces

class Child : MyInterface {


override fun bar() {
// body
}
}

Properties in Interfaces

You can declare properties in interfaces. A property declared in an interface can either
be abstract, or it can provide implementations for accessors. Properties declared in
interfaces can't have backing fields, and therefore accessors declared in interfaces can't
reference them.

interface MyInterface {
val prop: Int // abstract

val propertyWithImplementation: String


get() = "foo"

fun foo() {
print(prop)
}
}

class Child : MyInterface {


override val prop: Int = 29
}

Interfaces Inheritance

An interface can derive from other interfaces and thus both provide implementations for
their members and declare new functions and properties. Quite naturally, classes
implementing such an interface are only required to define the missing implementations:

interface Named {
val name: String
}

interface Person : Named {


val firstName: String
val lastName: String

override val name: String get() = "$firstName $lastName"


}

data class Employee(


// implementing 'name' is not required
override val firstName: String,
override val lastName: String,
val position: Position
) : Person
MODULE 2
Mobile Application Development 1
Subtopic 1
CLASSES
• Understand the concept of Classes
• Write program that uses classes
• Know the different applications of classes
Classes in Kotlin are declared using the keyword class:

class Invoice { /*...*/ }


The class declaration consists of the class name, the
class header 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.
class Empty
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 goes after the
class name (and optional type parameters).

class Person constructor(firstName: String) { /*...*/ }


If the primary constructor does not have any annotations
or visibility modifiers, the constructor keyword can be
omitted:
class Person(firstName: String) { /*...*/ }

The primary constructor cannot contain any code.


Initialization code can be placed in initializer blocks,
which are prefixed with the init keyword.
class InitOrderDemo(name: String) {
During an instance val firstProperty = "First property:
initialization, the $name".also(::println)

initializer blocks are init {


executed in the same println("First initializer block that prints ${name}")
}
order as they appear
in the class body, val secondProperty = "Second property:
interleaved with the ${name.length}".also(::println)

property initializers: init {


println("Second initializer block that prints
${name.length}")
}
}
Parameters of the primary constructor can be used in
the initializer blocks. They can also be used in property
initializers declared in the class body:
class Customer(name: String) {
val customerKey = name.toUpperCase()
}
For declaring properties and initializing them from the
primary constructor, Kotlin has a concise syntax:
class Person(val firstName: String, val lastName: String, var age: Int) {
/*...*/ }
You can use a trailing comma when you declare class
properties:
class Person(
val firstName: String,
val lastName: String,
var age: Int, // trailing comma
) { /*...*/ }
Much the same way as regular properties, the properties
declared in the primary constructor can be mutable (var)
or read-only (val).
If the constructor has annotations or visibility modifiers,
the constructor keyword is required, and the modifiers
go before it:

class Customer public @Inject constructor(name: String) {


/*...*/ }
The class can also declare secondary constructors,
which are prefixed with constructor:
class Person {
var children: MutableList<Person> = mutableListOf()
constructor(parent: Person) {
parent.children.add(this)
}
}
If the class has a primary constructor, each secondary
constructor needs to delegate to the primary
constructor, either directly or indirectly through another
secondary constructor(s). Delegation to another
constructor of the same class is done using the this
keyword: class Person(val name: String) {
var children: MutableList<Person> = mutableListOf()
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
Code in initializer blocks effectively class Constructors {
becomes part of the primary constructor. init {
Delegation to the primary constructor println("Init block")
happens as the first statement of a }
secondary constructor, so the code in all
initializer blocks and property initializers
is executed before the secondary constructor(i: Int) {
constructor body. Even if the class has println("Constructor")
no primary constructor, the delegation }
still happens implicitly, and the initializer }
blocks are still executed:
If a non-abstract class does not declare any constructors
(primary or secondary), it will have a generated primary
constructor with no arguments. The visibility of the
constructor will be public. If you do not want your class
to have a public constructor, you need to declare an
empty primary constructor with non-default visibility:

class DontCreateMe private constructor () { /*...*/ }


To create an instance of a class, we call the constructor
as if it were a regular function:
val invoice = Invoice()

val customer = Customer("Joe Smith")


Note that Kotlin does not have a new keyword.
• Constructors and initializer blocks
• Functions
• Properties
• Nested and Inner Classes
• Object Declarations
All classes in Kotlin have a common superclass Any,
that is the default superclass for a class with no
supertypes declared:

class Example // Implicitly inherits from Any

Any has three methods: equals(), hashCode() and


toString(). Thus, they are defined for all Kotlin classes.
By default, Kotlin classes are final: they can’t be
inherited. To make a class inheritable, mark it with the
open keyword.

open class Base //Class is open for inheritance


To declare an explicit supertype, place the type after a
colon in the class header:

open class Base(p: Int)


class Derived(p: Int) : Base(p)
If the derived class has a primary constructor, the base
class can (and must) be initialized right there, using the
parameters of the primary constructor.
If the derived class has no primary constructor, then each
secondary constructor has to initialize the base type using
the super keyword, or to delegate to another constructor
which does that. Note that in this case different secondary
constructors can call different constructors of the base type:
class MyView : View {
constructor(ctx: Context) : super(ctx)

constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)


}
As we mentioned before, we stick to making things explicit
in Kotlin. So, Kotlin requires explicit modifiers for
overridable members (we call them open) and for overrides:
open class Shape {
open fun draw() { /*...*/ }
fun fill() { /*...*/ }
}
class Circle() : Shape() {
override fun draw() { /*...*/ }
}
A member marked override is itself open, i.e. it may be
overridden in subclasses. If you want to prohibit re-
overriding, use final:

open class Rectangle() : Shape() {


final override fun draw() { /*...*/ }
}
Overriding properties works in a similar way to overriding methods;
properties declared on a superclass that are then redeclared on a
derived class must be prefaced with override, and they must have a
compatible type. Each declared property can be overridden by a
property with an initializer or by a property with a get method.
open class Shape {
open val vertexCount: Int = 0
}

class Rectangle : Shape() {


override val vertexCount = 4
}
You can also override a val
interface Shape {
property with a var property, val vertexCount: Int
but not vice versa. This is }
allowed because a val
class Rectangle(override val vertexCount: Int = 4) : Shape
property essentially // Always has 4 vertices
declares a get method, and
class Polygon : Shape {
overriding it as a var override var vertexCount: Int = 0 // Can be set to any
additionally declares a set number later
method in the derived class. }
open class Base(val name: String) {
init { println("Initializing Base") }
During construction of a open val size: Int =
new instance of a name.length.also { println("Initializing size in Base: $it") }
}
derived class, the base class Derived(
class initialization is name: String,
val lastName: String,
done as the first step ) : Base(name.capitalize().also { println("Argument for Base:
$it") }) {
and thus happens before init { println("Initializing Derived") }
the initialization logic of override val size: Int =
(super.size + lastName.length).also { println("Initializing
the derived class is run. size in Derived: $it") }
}
Code in a derived open class Rectangle {
open fun draw() { println("Drawing a rectangle") }
class can call its val borderColor: String get() = "black"
superclass }
functions and
property accessors class FilledRectangle : Rectangle() {
override fun draw() {
implementations super.draw()
using the super println("Filling the rectangle")
keyword: }

val fillColor: String get() = super.borderColor


}
class FilledRectangle: Rectangle() {
Inside an inner override fun draw() {
val filler = Filler()
class, accessing filler.drawAndFill()
the superclass of }

the outer class is inner class Filler {


done with the fun fill() { println("Filling") }
fun drawAndFill() {
super keyword super@FilledRectangle.draw() // Calls Rectangle's
qualified with the implementation of draw()
fill()
outer class name: println("Drawn a filled rectangle with color
${super@FilledRectangle.borderColor}") // Uses Rectangle's
super@Outer: implementation of borderColor's get()
}
}
}
If a class inherits multiple implementations of the same
member from its immediate superclasses, it must
override this member and provide its own
implementation. To denote the supertype from which the
inherited implementation is taken, we use super qualified
by the supertype name in angle brackets, e.g.
super<Base>, (see next slide for example)
open class Rectangle {
open fun draw() { /* ... */ }
}

interface Polygon {
fun draw() { /* ... */ } // interface members are 'open' by default
}
class Square() : Rectangle(), Polygon {
// The compiler requires draw() to be overridden:
override fun draw() {
super<Rectangle>.draw() // call to Rectangle.draw()
super<Polygon>.draw() // call to Polygon.draw()
}
}
A class and some of its members may be declared
abstract. An abstract member does not have an
implementation in its class. Note that we do not need to
annotate an abstract class or function with open – it
goes without saying.
We can override a non-abstract open member with an
abstract one:
open class Polygon {
open fun draw() {}
}

abstract class Rectangle : Polygon() {


abstract override fun draw()
}
Properties in Kotlin classes can be declared either as
mutable using the var keyword, or as read-only using the
val keyword.
class Address {
var name: String = "Holmes, Sherlock“
var street: String = "Baker“
var city: String = "London“
var state: String? = null
var zip: String = "123456“
}
To use a property, simply refer to it by name:

fun copyAddress(address: Address): Address {


val result = Address() // there's no 'new' keyword in Kotlin
result.name = address.name // accessors are called
result.street = address.street
// ...
return result
}
The full syntax for declaring a property is:

var <propertyName>[: <PropertyType>] [= <property_initializer>]


[<getter>]
[<setter>]

The initializer, getter and setter are optional. Property type is


optional if it can be inferred from the initializer.
We can define custom accessors for a property. If we
define a custom getter, it will be called every time we
access the property:

val isEmpty: Boolean


get() = this.size == 0
If we define a custom setter, it will be called every time
we assign a value to the property. A custom setter looks
like this:
var stringRepresentation: String
get() = this.toString()
set(value) {// the name of the setter parameter is value by
convention
setDataFromString(value) // parses the string and assigns
values to other properties
}
Interfaces in Kotlin can contain declarations of abstract
methods, as well as method implementations. What
makes them different from abstract classes is that
interfaces cannot store state. They can have properties
but these need to be abstract or to provide accessor
implementations.
An interface is defined using the keyword interface:

interface MyInterface {
fun bar()
fun foo() {
// optional body
}
}
A class or object can implement one or more interfaces:

class Child : MyInterface {


override fun bar() {
// body
}
}
You can declare properties in interfaces. A property
declared in an interface can either be abstract, or it can
provide implementations for accessors. Properties
declared in interfaces can't have backing fields, and
therefore accessors declared in interfaces can't
reference them. (See next slide for sample code)
interface MyInterface {
val prop: Int // abstract
val propertyWithImplementation: String
get() = "foo“
fun foo() {
print(prop)
}
}​
class Child : MyInterface {
override val prop: Int = 29
}
An interface can derive from other interfaces and thus
both provide implementations for their members and
declare new functions and properties. Quite naturally,
classes implementing such an interface are only
required to define the missing implementations. (See
next slide for sample code)
interface Named {
val name: String
}
interface Person : Named {
val firstName: String
val lastName: String
override val name: String get() = "$firstName $lastName“
}​
data class Employee(
// implementing 'name' is not required
override val firstName: String,
override val lastName: String,
val position: Position) : Person
• Griffiths, D., Griffiths, D. (2019). Head First Kotlin. O'Reilly Media, Inc.
• Jemerov, D., and Isakova, S. (2017). Kotlin in Action. Manning Publications
• Horton, J. (2018). Android Programming for Beginners: Build in-depth, full-featured Android 9 Pie apps starting from
zero programming experience, 2nd Edition. Packt Publishing
• Franceschi, H. J. (2018). Android App Development 1st Edition.
• Iversen, J., Eierman, M. (2018). Mobile App Development for iOS and Android, Edition 2.0. Prospect Press, Inc.
• Phillips, B., Stewart, C., Marsicano, K. (2017). Android Programming: The Big Nerd Ranch Guide (3rd Edition) (Big Nerd
Ranch Guides) 3rd Edition.
• Dawn, G. (2017). Head First Android Development: A Brain-Friendly Guide. O' Reilly Media, Inc.
• Drongelen, M., Dennis, A., Garabedian, R. (2017). Lean Mobile App Development: Apply Lean startup methodologies to
develop successful iOS and Android apps. Packt Publishing
• Miller, C. (2016), Cross-platform Localization for Native Mobile Apps with Xamarin
• Beer, Paula(2015), Hello app inventor: android programming for kids and the rest of us.
• Deborah Gonzalez (2015), Managing Online Risk: Apps, Mobile, And Social Media Security

Online Sources:
https://kotlinlang.org/docs/reference/
http://www.developer.android.com
http://www.android.com
http://www.onefeu.instructure.com
Mobile Application
Development 1
Module 2
CLASSES AND OBJECTS
• Understand the concept of Classes
• Write program that uses classes
• Know the different applications of classes
• Understand the concept of Objects
• Write program that uses objects
• Know the different applications of objects
Classes in Kotlin are declared using the keyword class:

class Invoice { /*...*/ }


The class declaration consists of the class name, the
class header 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.
class Empty
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 goes after the
class name (and optional type parameters).

class Person constructor(firstName: String) { /*...*/ }


If the primary constructor does not have any annotations
or visibility modifiers, the constructor keyword can be
omitted:
class Person(firstName: String) { /*...*/ }

The primary constructor cannot contain any code.


Initialization code can be placed in initializer blocks,
which are prefixed with the init keyword.
class InitOrderDemo(name: String) {
During an instance val firstProperty = "First property:
initialization, the $name".also(::println)

initializer blocks are init {


executed in the same println("First initializer block that prints ${name}")
}
order as they appear
in the class body, val secondProperty = "Second property:
interleaved with the ${name.length}".also(::println)

property initializers: init {


println("Second initializer block that prints
${name.length}")
}
}
Parameters of the primary constructor can be used in
the initializer blocks. They can also be used in property
initializers declared in the class body:
class Customer(name: String) {
val customerKey = name.toUpperCase()
}
For declaring properties and initializing them from the
primary constructor, Kotlin has a concise syntax:
class Person(val firstName: String, val lastName: String, var age: Int) {
/*...*/ }
You can use a trailing comma when you declare class
properties:
class Person(
val firstName: String,
val lastName: String,
var age: Int, // trailing comma
) { /*...*/ }
Much the same way as regular properties, the properties
declared in the primary constructor can be mutable (var)
or read-only (val).
If the constructor has annotations or visibility modifiers,
the constructor keyword is required, and the modifiers
go before it:

class Customer public @Inject constructor(name: String) {


/*...*/ }
The class can also declare secondary constructors,
which are prefixed with constructor:
class Person {
var children: MutableList<Person> = mutableListOf()
constructor(parent: Person) {
parent.children.add(this)
}
}
If the class has a primary constructor, each secondary
constructor needs to delegate to the primary
constructor, either directly or indirectly through another
secondary constructor(s). Delegation to another
constructor of the same class is done using the this
keyword: class Person(val name: String) {
var children: MutableList<Person> = mutableListOf()
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
Code in initializer blocks effectively class Constructors {
becomes part of the primary constructor. init {
Delegation to the primary constructor println("Init block")
happens as the first statement of a }
secondary constructor, so the code in all
initializer blocks and property initializers
is executed before the secondary constructor(i: Int) {
constructor body. Even if the class has println("Constructor")
no primary constructor, the delegation }
still happens implicitly, and the initializer }
blocks are still executed:
If a non-abstract class does not declare any constructors
(primary or secondary), it will have a generated primary
constructor with no arguments. The visibility of the
constructor will be public. If you do not want your class
to have a public constructor, you need to declare an
empty primary constructor with non-default visibility:

class DontCreateMe private constructor () { /*...*/ }


To create an instance of a class, we call the constructor
as if it were a regular function:
val invoice = Invoice()

val customer = Customer("Joe Smith")


Note that Kotlin does not have a new keyword.
• Constructors and initializer blocks
• Functions
• Properties
• Nested and Inner Classes
• Object Declarations
All classes in Kotlin have a common superclass Any,
that is the default superclass for a class with no
supertypes declared:

class Example // Implicitly inherits from Any

Any has three methods: equals(), hashCode() and


toString(). Thus, they are defined for all Kotlin classes.
By default, Kotlin classes are final: they can’t be
inherited. To make a class inheritable, mark it with the
open keyword.

open class Base //Class is open for inheritance


To declare an explicit supertype, place the type after a
colon in the class header:

open class Base(p: Int)


class Derived(p: Int) : Base(p)
If the derived class has a primary constructor, the base
class can (and must) be initialized right there, using the
parameters of the primary constructor.
If the derived class has no primary constructor, then each
secondary constructor has to initialize the base type using
the super keyword, or to delegate to another constructor
which does that. Note that in this case different secondary
constructors can call different constructors of the base type:
class MyView : View {
constructor(ctx: Context) : super(ctx)

constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)


}
As we mentioned before, we stick to making things explicit
in Kotlin. So, Kotlin requires explicit modifiers for
overridable members (we call them open) and for overrides:
open class Shape {
open fun draw() { /*...*/ }
fun fill() { /*...*/ }
}
class Circle() : Shape() {
override fun draw() { /*...*/ }
}
A member marked override is itself open, i.e. it may be
overridden in subclasses. If you want to prohibit re-
overriding, use final:

open class Rectangle() : Shape() {


final override fun draw() { /*...*/ }
}
Overriding properties works in a similar way to overriding methods;
properties declared on a superclass that are then redeclared on a
derived class must be prefaced with override, and they must have a
compatible type. Each declared property can be overridden by a
property with an initializer or by a property with a get method.
open class Shape {
open val vertexCount: Int = 0
}

class Rectangle : Shape() {


override val vertexCount = 4
}
You can also override a val
interface Shape {
property with a var property, val vertexCount: Int
but not vice versa. This is }
allowed because a val
class Rectangle(override val vertexCount: Int = 4) : Shape
property essentially // Always has 4 vertices
declares a get method, and
class Polygon : Shape {
overriding it as a var override var vertexCount: Int = 0 // Can be set to any
additionally declares a set number later
method in the derived class. }
open class Base(val name: String) {
init { println("Initializing Base") }
During construction of a open val size: Int =
new instance of a name.length.also { println("Initializing size in Base: $it") }
}
derived class, the base class Derived(
class initialization is name: String,
val lastName: String,
done as the first step ) : Base(name.capitalize().also { println("Argument for Base:
$it") }) {
and thus happens before init { println("Initializing Derived") }
the initialization logic of override val size: Int =
(super.size + lastName.length).also { println("Initializing
the derived class is run. size in Derived: $it") }
}
Code in a derived open class Rectangle {
open fun draw() { println("Drawing a rectangle") }
class can call its val borderColor: String get() = "black"
superclass }
functions and
property accessors class FilledRectangle : Rectangle() {
override fun draw() {
implementations super.draw()
using the super println("Filling the rectangle")
keyword: }

val fillColor: String get() = super.borderColor


}
class FilledRectangle: Rectangle() {
Inside an inner override fun draw() {
val filler = Filler()
class, accessing filler.drawAndFill()
the superclass of }

the outer class is inner class Filler {


done with the fun fill() { println("Filling") }
fun drawAndFill() {
super keyword super@FilledRectangle.draw() // Calls Rectangle's
qualified with the implementation of draw()
fill()
outer class name: println("Drawn a filled rectangle with color
${super@FilledRectangle.borderColor}") // Uses Rectangle's
super@Outer: implementation of borderColor's get()
}
}
}
If a class inherits multiple implementations of the same
member from its immediate superclasses, it must
override this member and provide its own
implementation. To denote the supertype from which the
inherited implementation is taken, we use super qualified
by the supertype name in angle brackets, e.g.
super<Base>, (see next slide for example)
open class Rectangle {
open fun draw() { /* ... */ }
}

interface Polygon {
fun draw() { /* ... */ } // interface members are 'open' by default
}
class Square() : Rectangle(), Polygon {
// The compiler requires draw() to be overridden:
override fun draw() {
super<Rectangle>.draw() // call to Rectangle.draw()
super<Polygon>.draw() // call to Polygon.draw()
}
}
A class and some of its members may be declared
abstract. An abstract member does not have an
implementation in its class. Note that we do not need to
annotate an abstract class or function with open – it
goes without saying.
We can override a non-abstract open member with an
abstract one:
open class Polygon {
open fun draw() {}
}

abstract class Rectangle : Polygon() {


abstract override fun draw()
}
Properties in Kotlin classes can be declared either as
mutable using the var keyword, or as read-only using the
val keyword.
class Address {
var name: String = "Holmes, Sherlock“
var street: String = "Baker“
var city: String = "London“
var state: String? = null
var zip: String = "123456“
}
To use a property, simply refer to it by name:

fun copyAddress(address: Address): Address {


val result = Address() // there's no 'new' keyword in Kotlin
result.name = address.name // accessors are called
result.street = address.street
// ...
return result
}
The full syntax for declaring a property is:

var <propertyName>[: <PropertyType>] [= <property_initializer>]


[<getter>]
[<setter>]

The initializer, getter and setter are optional. Property type is


optional if it can be inferred from the initializer.
We can define custom accessors for a property. If we
define a custom getter, it will be called every time we
access the property:

val isEmpty: Boolean


get() = this.size == 0
If we define a custom setter, it will be called every time
we assign a value to the property. A custom setter looks
like this:
var stringRepresentation: String
get() = this.toString()
set(value) {// the name of the setter parameter is value by
convention
setDataFromString(value) // parses the string and assigns
values to other properties
}
Interfaces in Kotlin can contain declarations of abstract
methods, as well as method implementations. What
makes them different from abstract classes is that
interfaces cannot store state. They can have properties
but these need to be abstract or to provide accessor
implementations.
An interface is defined using the keyword interface:

interface MyInterface {
fun bar()
fun foo() {
// optional body
}
}
A class or object can implement one or more interfaces:

class Child : MyInterface {


override fun bar() {
// body
}
}
You can declare properties in interfaces. A property
declared in an interface can either be abstract, or it can
provide implementations for accessors. Properties
declared in interfaces can't have backing fields, and
therefore accessors declared in interfaces can't
reference them. (See next slide for sample code)
interface MyInterface {
val prop: Int // abstract
val propertyWithImplementation: String
get() = "foo“
fun foo() {
print(prop)
}
}​
class Child : MyInterface {
override val prop: Int = 29
}
An interface can derive from other interfaces and thus
both provide implementations for their members and
declare new functions and properties. Quite naturally,
classes implementing such an interface are only
required to define the missing implementations. (See
next slide for sample code)
interface Named {
val name: String
}
interface Person : Named {
val firstName: String
val lastName: String
override val name: String get() = "$firstName $lastName“
}​
data class Employee(
// implementing 'name' is not required
override val firstName: String,
override val lastName: String,
val position: Position) : Person
To create an object of an anonymous class that inherits
from some type (or types), we write:
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) { /*...*/ }​
override fun mouseEntered(e: MouseEvent) { /*...*/ }
})
If a supertype has a open class A(x: Int) {
constructor, appropriate public open val y: Int = x
constructor parameters }​
must be passed to it.
Many supertypes may interface B { /*...*/ }
be specified as a
val ab: A = object : A(1), B {
comma-separated list
override val y = 15
after the colon: }
If, by any chance, we need "just an object", with no
nontrivial supertypes, we can simply say:
fun foo() {
val adHoc = object {
var x: Int = 0
var y: Int = 0
}
print(adHoc.x + adHoc.y)
}
Anonymous objects can be used as types only in local
and private declarations. If you use an anonymous
object as a return type of a public function or the type of
a public property, the actual type of that function or
property will be the declared supertype of the
anonymous object, or Any if you didn't declare any
supertype. Members added in the anonymous object will
not be accessible. (See next slide for sample code)
class C {
// Private function, so the return type is the anonymous object type
private fun foo() = object {
val x: String = "x“
}
// Public function, so the return type is Any
fun publicFoo() = object {
val x: String = "x“
}
fun bar() {
val x1 = foo().x // Works
val x2 = publicFoo().x // ERROR: Unresolved reference 'x’
}
}
The code in fun countClicks(window: JComponent) {
var clickCount = 0
object var enterCount = 0
expressions window.addMouseListener(object : MouseAdapter() {
can access override fun mouseClicked(e: MouseEvent) {
clickCount++
variables from }​
the enclosing override fun mouseEntered(e: MouseEvent) {
enterCount++
scope. }
})
// ...
}
Singleton may
object DataProviderManager {
be useful in fun registerDataProvider(provider: DataProvider) {
several cases, // ...
and Kotlin }
(after Scala) val allDataProviders: Collection<DataProvider>
makes it easy get() = // ...
to declare }
singletons:
What you see in the code in previous slide is called an
object declaration, and it always has a name following
the object keyword. Just like a variable declaration, an
object declaration is not an expression, and cannot be
used on the right hand side of an assignment statement.
An object declaration inside a class can be marked with
the companion keyword:

class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
Members of the companion object can be called by using
simply the class name as the qualifier:
val instance = MyClass.create()
The name of the companion object can be omitted, in
which case the name Companion will be used:
class MyClass {
companion object { }
}​
val x = MyClass.Companion
The name of a class class MyClass1 {
used by itself (not as a companion object Named { }
qualifier to another }​
name) acts as a
reference to the val x = MyClass1​class MyClass2 {
companion object of the companion object { }
class (whether named or }​
not):
val y = MyClass2
Even though the interface Factory<T> {
members of companion fun create(): T
objects look like static }
members in other
languages, at runtime class MyClass {
those are still instance companion object : Factory<MyClass> {
members of real override fun create(): MyClass = MyClass()
objects, and can, for }
example, implement }
interfaces: val f: Factory<MyClass> = MyClass
• Griffiths, D., Griffiths, D. (2019). Head First Kotlin. O'Reilly Media, Inc.
• Jemerov, D., and Isakova, S. (2017). Kotlin in Action. Manning Publications
• Horton, J. (2018). Android Programming for Beginners: Build in-depth, full-featured Android 9 Pie apps starting from
zero programming experience, 2nd Edition. Packt Publishing
• Franceschi, H. J. (2018). Android App Development 1st Edition.
• Iversen, J., Eierman, M. (2018). Mobile App Development for iOS and Android, Edition 2.0. Prospect Press, Inc.
• Phillips, B., Stewart, C., Marsicano, K. (2017). Android Programming: The Big Nerd Ranch Guide (3rd Edition) (Big Nerd
Ranch Guides) 3rd Edition.
• Dawn, G. (2017). Head First Android Development: A Brain-Friendly Guide. O' Reilly Media, Inc.
• Drongelen, M., Dennis, A., Garabedian, R. (2017). Lean Mobile App Development: Apply Lean startup methodologies to
develop successful iOS and Android apps. Packt Publishing
• Miller, C. (2016), Cross-platform Localization for Native Mobile Apps with Xamarin
• Beer, Paula(2015), Hello app inventor: android programming for kids and the rest of us.
• Deborah Gonzalez (2015), Managing Online Risk: Apps, Mobile, And Social Media Security

Online Sources:
https://kotlinlang.org/docs/reference/
http://www.developer.android.com
http://www.android.com
http://www.onefeu.instructure.com

You might also like