Properties, OOP, Conventions
Properties
class Contact(
val name: String,
val address: String
)
// call the variable directly
contact.address
Fields
- You can access field only inside accessors
class StateLogger {
var state = false
set(value) {
println("state has changed: " + "$field -> $value")
field = value
}
}
- No mentioning of
fieldin customer accessors
...
class StateLogger{
private var boolState = false
var state: State
get() = if(boolState) ON else OFF
...
}
- You always use property instead of getter and setters
- Changing visibility of a setter
// change visibility of the setter but use its default implementation
class LengthCounter {
var counter: Int = 0
private set
}
Property in interface
interface User {
val nickname: String
}
- Which property is calculated on each access?
interface User {
val nickname: String
}
class FacebookUser(val accountId: Int) : User {
override val nickname = getFacebookName(accountId)
}
class SubscribingUser(val email: String) : User {
override val nickname: String
get() = email.substringBefore('@')
}
- Answer:
SubscribingUser.nickname - Open property can't be used in smart casts
- Thus, the below code is correct approach:
interface Session {
val user: User
}
fun analyzeUserSession(session: Session){
val user = session.user
if(user is FacebookUser){
println(user.accountId)
}
}
Extension Properties
val String.lastIndex: Int
get() = this.length - 1
val String.indices: IntRange
get() = 0..lastIndex
Lazy Property
val lazyValue: String by lazy {
println("computed")
"Hello"
}
fun main(args: Array<String>){
println(lazyValue)
println(lazyValue)
}
// here is what is printed
// computed
// Hello
// Hello
lateinit
- Use
lateinitvalue as a value of a non-nullable type, so no need to use ternary?when referring to it anymore.- i.e.
myData?.foois not used. Just usemyData.foo.
- i.e.
class KotlinActivity: Activity() {
lateinit var myData: MyData
override fun onCreate(savedInstanceState: Bundle?){
super.onCreate(savedInstanceState)
myData = intent.getParcelableExtra("MY_DATA")
}
...myData.foo
}
warning
- lateinit variables cannot be
val. It has to bevar - lateinit variables cannot be
null - lateinit variables cannot be primitive type
Object Oriented Programming
- Any declaration is
publicandfinalby default - To make something non-final, mark it as
open - There is no package private visibility.
- It's called
internal
- It's called
Module
- a set of Kotlin files compiled together
- Maven
- Gradle
Definitions
finalmodifier means cannot be overridden.openmodifier means can be overridenabstractmodifier means must be overriden (can't have an implementation)overridemodifier means overrides a member in a superclass or interface (mandatory to do)publicclass member or top-level declaration is visible everywhere.internalclass member or top-level declaration is visible in the module.protectedclass member is visible in the class and its subclassesprivateclass member is visible in the class onlyprivatetop-level declaration is visible in the same file.
Constructors
class A
val a = A()
Concise primary constructor
class Person(val name: String, val age: Int)
Full primary constructor syntax
class Person(name: String){
val name: String
init {
this.name = name
}
}
is equivalent to:
class Person(val name: String)
Changing visibility of a constructor
class InternalComponent
internal constructor(name: String){
...
}
Secondary Constructors
class Rectangle(val height: Int, val width: Int){
constructor(side: Int) : this(side, side) {...}
}
Different syntax for inheritence
Same syntax for extends & implements
interface Base
class BaseImpl: Base
open class Parent
class Child: Parent()
What will be printed?
open class Parent {
open val foo = 1
init {
println(foo)
}
}
class Child: Parent() {
override val foo = 2
}
fun main() {
Child()
}
- Answer: 0
Class Modifiers
enum Class
- represents enumeration
import Color.*
enum class Color{
BLUE, ORANGE, RED
}
fun getDescription(color: Color) =
when (color) {
BLUE -> "cold"
ORANGE -> "mild"
RED -> "hot"
}
enum class with Properties
enum class Color(val r: Int, val g: Int, val b: Int){
BLUE(0, 0, 255), ORANGE(255, 165, 0), RED(255, 0, 0);
fun rgb() = (r * 256 + g) * 256 + b
}
println(BLUE.r) // 0
println(BLUE.rgb()) //255
data modifier
- Generates useful methods:
equalshashCodecopytoString
What will be printed?
class Foo(val first: Int, val second: Int)
data class Bar(val first: Int, val second: Int)
val f1 = Foo(1, 2)
val f2 = Foo(1, 2)
println(f1 == f2)
val b1 = Bar(1, 2)
val b2 = Bar(1, 2)
println(b1 == b2)
- Answer:
false true - In the Foo, you are checking reference equality
- In the Bar, since it is a data class, you are comparing content so it's true.
Properties in primary constructor
data class User(val email: String){
var nickname: String? = null
}
val user1 = User("voldemort@gmail.com")
user1.nickname = "Voldemort"
val user2 = User("voldemort@gmail.com")
user2.nickname = "Hello"
// user1 == user2 is true
// because only the email values are compared and not the nicknames
Sealed Class
- Restricts class hierarchy: all subclasses must be located in the same file
sealed class Expr
class Num(val value: Int): Expr()
class Sum(val left: Expr, val right: Expr): Expr()
fun eval(e: Expr): Int = when (e) {
is Num -> e.value
is Sum -> eval(e.left) + eval(e.right)
}
inner modifier
- The inner class stores a reference to an outer class.
class A {
class B
inner class C{
...this@A...
}
}
Class Delegation
class Controller(
repository: Repository,
logger: Logger
) : Repository by repository, Logger by logger
Objects
object = singleton
object expressions
window.addMouseListener(
object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
// ...
}
override fun mouseEntered(e: MouseEvent) {
// ...
}
}
)
- An object expression is not a singleton.
- A new instance of object expression is created for each call.
companion object: special object inside a class- it could implement an interface
- can be a receiver of extension function
No static keyword
- Declare "static" members:
- at the top level
- inside Objects
- inside companion objects
Which line will not compile?
class C {
companion object {
@JvmStatic fun foo() {}
fun bar() {}
}
}
// Java
C.foo(); //#1
C.bar(); //#2
C.Companion.foo(); //#3
C.Companion.bar(); //#4
- Answer: line 2 because you try to access bar as a static function. By default, it cannot be accessed like that.
- Is it possible to declare an inner object?
class A {
inner object B
}
- Answer: No. The compiler gives you an error because inner is not applicable to object.
Nested object
- This is allowed:
class A {
object B
}
Constants
constfor primitive types and String@JvmFieldeliminates accessors- exposes a Kotlin property as a field in Java
Compile time Constants
const val answer = 42
@JvmField
object A {
@JvmField
val prop = MyClass() //static field generated
}
class B {
@JvmField
val prop = MyClass() //regular field generated
}
- Which declaration will expose answer as static field?
// Answer:
@JvmField
val answer
const val answer = 42
- Which declaration(s) will inline the value of answer in the resulting bytecode?
object SuperComputer {
val answer = 42
}
-
Answer:
const val answer = 42, the const achieves this. -
Which declaration(s) will expose a top-level property as static field when used from Java?
val answer = 42
- Answer:
// Answer:
@JvmField
val answer
const val answer = 42
Generics
interface List<E>{
fun get(index: Int): E
}
fun foo(ints:List<Int>) {...}
fun bar(strings: List<String>) {...}
Generic Functions
fun <T> List<T>.filter(predicate: (T) -> Boolean): List<T>
Nullable Generic Argument
fun <T> List<T>.firstOrNull(): T?
val ints = listOf(1, 2, 3)
val i: Int? = ints.firstOrNull() // output: 1
val j: Int? = listOf<Int>().firstOrNull() // null
val k: Int? = listOf(null, 1).firstOrNull() //null
Can element be nullable in the example below?
fun <T> foo(list: List<T>) {
for (element in list) {
}
}
- Answer: yes. You can pass a list of nullable elements at an argument, so the element can be null.
Non-nullable upper bound
fun <T: Any> foo(list: List<T>){
for(element in list){
}
}
Comparable upper bound
fun<T: Comparable<T>> max(first: T, second: T): T {
return if(first > second) first else second
}
max(1, 3) // output: 3
Using @JvmName
fun List<Int>.average(): Double {...}
// Modifies the file name under the hood and modifies the name of the function at the bytecode
@JvmName("averageOfDouble")
fun List<Double>.average(): Double {...}
- The two functions above are considered two different functions.
- In Java, you call the the second function as "averageOfDouble"
OOP Design of Kotlin
publicis default and very permissivefinalis the most restrictive: no one can override this- enables smart casts
- e.g. have a val that is a member in a cast
- If it's
open, you can't smart cast it.
Conventions
Operator Overloading
a + ba.plus(b)
operator fun Point.plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
Unary operations
-ais the same asa.unaryMinus()
operator fun Point.unaryMinus() = Point(-x, -y)
Assignment Operations
a += b;
can be the same as
a = a.plus(b);
a.plusAssign(b);
What will be printed?
val list1 = listOf(1, 2, 3)
var list2 = list1
list2 += 4
println(list1)
println(list2)
- Answer:
[1, 2, 3]
[1, 2, 3, 4]
Comparisons
a >= bis about the same asa.compareTo(b) >= 0
Equality Check
s1 == s2is same ass1.equals(s2)
Accessing Elements by index
x[a, b]is same asx.get(a, b)x[a, b] = cis same asx.set(a, b, c)
Which function allows using the following syntax for your custom class?
val board : Board = ...
board[1, 2] = 'x'
- Answer:
operator fun Board.set(x: Any, y: Any, value: Char) { ... }
- You need the
operatorkeyword to make this index work.
Which elements can be compared using comparison operations?
x < y
x >= y
-
Answer:
Strings- elements implementing
Comparableinterface - elements that define member or extenson
operatorfunctioncompareto(with the right signature)
-
Implement 'equals2' without using '==' so that it was equivalent to 'equals1'. You can call 'equals()' directly and use the reference equality operator '==='.
data class Value(val s: String)
fun equals1(v1: Value?, v2: Value?): Boolean {
return v1 == v2
}
fun equals2(v1: Value?, v2: Value?): Boolean {
if (v1 === v2) return true // Reference equality check
if (v1 == null || v2 == null) return false // One is null and the other is not
return v1.equals(v2) // Calls the equals() method
}
fun main(args: Array<String>) {
println(equals1(Value("abc"), Value("abc")) == true) // should be true
println(equals1(Value("abc"), null) == false) // should be false
println(equals1(null, Value("abc")) == false) // should be false
println(equals1(null, null) == true) // should be true
println(equals2(Value("abc"), Value("abc")) == true) // should be true
println(equals2(Value("abc"), null) == false) // should be false
println(equals2(null, Value("abc")) == false) // should be false
println(equals2(null, null) == true) // should be true
}
Operator overloading (When not to use it)
- You cannot invent your own operator
- You cannot alter precedence of existing operators
- Only Allowed to overload a predefined set of operators