Android Basics: Kotlin(1)

1. Variables

1-1 'val' and 'var'

  • var is mutable, can be changed during execution
  • val is immutable, cannot be changed once initialized
1
2
3
4
5
6
//OK
var myName = "Cherry"
myName = "newName"
//NG
val myName = "Cherry"
myName = "newName"

1-2 Data Types

  • kotlin is a typed language
  • no need to always state the type because of the type inference

integer types

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
  //Int requires 32 bit
  var myInt = 999999999

  //explicit type declaration
  //8 bit
  var myByte : Byte = 99
  //16 bit
  var myShort : Short = 9999
  //64 bit
  var myLong : Long = 12_039_812_309_487_120_99

  //no type annotation
  var myInt //!error. must either have type annotation or be initialized 

Floating point number types

1
2
  var myFloat : Float = 12.34F
  var myDouble = 123.34566 //always assumes to be double

Bool, Char and String

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
  //Boolean
  var myBool = true
  myBool = false
  //Char
  var myChar = '$' //no more than 1 sign

  //String
  val myString = "Hello"
  val firstChar = myString[0]
  val lastChar = myString[myString.length - 1]
  //String Interpolation
  print("$firstChar $lastChar") //H o

2. Operators

2-1 Arithmetic operators

1
2
3
4
5
6
7
8
  var result = 3 + 2
  // Assignment operators
  result *= 2
  result /= 2
  result += 2
  result -= 2
  result %= 2 // get remainder
  print(result) //5

2-2 Comparison operator

  • ==, !-, <=, >=...

2-3 increment, decrement

  • --, ++ (before use or after use)

3. Control flows

3-1 if-else if-else, when

1
2
3
4
5
6
7
  if (...) {
    ...
  } else if (...) {
    ...
  } else (...) {
    ...
  }

when

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
  var season = 3
  when (season) {
      1 -> println("Spring")
      2 -> println("Summer")
      3 -> println("Autumn")
      4 -> println("Winter")
      else -> println("Invalid")
  }
  //with range
  var month = 2
  when (month) {
    in 3..5 -> println("Spring")
    in 6..8 -> println("Summer")
    in 9..11 -> println("Autumn")
    1,2,12 -> println("Winter")
    else -> println("Invalid")
  }

  //with opposite range
  var age = 18
  when (age) {
      !in 0..17 -> println("Adult")
      else -> println("Kid")
  }

  //check type
  var number : Any = 18.23
  when (number) {
      is Int -> println("$number is a Int")
      is Double -> println("$number is a double")
      else -> println("$number is none of the above")
  }

3-2 while

while

1
2
3
4
5
  var x = 1
  while (x <= 10) {
      println("$x")
      x++
  }

do-while

1
2
3
4
5
  var x = 15
  do {
      println("$x") //will be executed at least once
      x++
  } while (x <= 10)

3-3 for

1
2
3
  for (num in 1..10) {
      print("$num ") //print 1~10
  }
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
  for (num in 1 until 10) {
      print("$num ") //print 1~9 (excluding 10)
  }

  for (num in 10 downTo 1) {
      print("$num ") //print 10~1
  }

  for (num in 10 downTo 1 step 2) {
      print("$num ") //10 8 6 4 2 
  }

4. Functions

  • Difference between argument and parameter?
  • Difference between method and function? -- A method is a function within a class
1
2
3
4
5
6
7
8
fun main() {
    var result = avg(3.1,6.7)
    print("result is: $result")
}

fun avg(a: Double, b: Double): Double {
    return (a + b) / 2
}

5. Nullable

5-1 Null-safe operator

  • ?
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
  var nonNullString = "can not be null"
  nonNullString = null //compile error

  var nullableString : String? = "can not be null"
  nullableString = null //OK

  var len = nullableString.length //compile error
  var len = nullableString?.length //OK. if null returns null

  //Can be executed only if nullableString is not null.
  //Comment out 'nullableString = null' makes this work.
  nullableString?.let {println(it.length)} //compile error. 

5-2 Elvis operator

  • ?:
1
2
3
  var nullableString: String? = "can be null"
  // if null, assign the default value
  val nonNullValue = nullableString ?: "Default"
  • !! converts a nullable variable to a non-null variable (will throw an NPE if the nullable variable holds a null value)
1
  nullableString!!.uppercase()
  • null save operator in a chain
1
  var wifeAge : String? = user?.wife?.age ?: "0"

6. OOP in kotlin

6-1 Class

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
fun main() {
    var phone1 = MobilePhone(brand = "Samsung", model = null)
}

//osName has default value
class MobilePhone(osName : String = "Android", brand : String?, model : String?) {
  //init blog
    init {
        println("OS name: $osName, brand: $brand, model : $model") 
        //OS name: Android, brand: Samsung, model : null
    }
}

6-2 Scope and shadowing

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
  fun main() {
    testScope("test")
  }

  fun testScope(a: String) {
      //a = "new value" //parameter cannot be reassigned
      var a = "another value" //OK.
      //using the same name as the parameter is allowed, which is called shadowing
      //but this will cause confusion, and the original parameter will never be able to be accessed
  }

6-3 Member variables

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
  fun main() {
      var mrWong = Person(firstName = "lao", lastName = "Wong")
      mrWong.stateAge() //lao・Wong's age is null

      var mrZhang = Person(firstName = "lao", lastName = "Zhang", age = 50)
      mrZhang.stateAge() //lao・Zhang's age is 50

      var mrDoe = Person(age = 20)
      mrDoe.stateAge() //John・Doe's age is 20
  }

  /*
    parameters in the constructor are visible to init block and other constructors
    but are not visible to normal methods.
  */
  class Person(firstName : String = "John", lastName : String = "Doe") {
      //Member Variables - properties
      //Sometimes holds an value but not always (firstName & lastName are necessary)
      var age : Int? = null
      var firstName : String = "John" //accessible from methods
      var lastName : String = "Doe"

      init {
          this.firstName = firstName
          this.lastName = lastName
          println("Initialized a new Person instance with: " +
          "first name is $firstName, last name is $lastName")
      }

      //Member constructor
      //this represents the primary constructor
      constructor(firstName : String, lastName : String, age: Int) : this(firstName, lastName) {
          //this 'this' is just a normal this..
          this.firstName = firstName
          this.lastName = lastName
          this.age = age
      }
      
      constructor(age: Int) : this() {
          this.age = age
      }

      //Member functions - Methods
      fun stateAge() {
          println("$firstName$lastName\'s age is $age")
      }

  }

6-4 Lateinit & Getter Setter

  • Be careful before you have initialized lateinit variables
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
  fun main() {
      var myCar = Car()
      myCar.owner //UninitializedPropertyAccessException
  }

  class Car() {
      //I will initialize it later on!
      lateinit var owner : String

      //↓Invoke this will solve the error
      //init {
      //  this.owner = "laoWong"
      //}
  }
  • Getter
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
  fun main() {
      var myCar = Car()
      print("the brand is: ${myCar.brand}")//the brand is: bmw
  }

  class Car() {
      var brand: String = "BMW"
          // custom getter
          get() {
              return field.lowercase()
          }
  }
  • Actually kotlin is generating getter/setter for us
1
2
3
4
5
6
7
8
9
  var maxSpeed : Int = 250
      //↓Auto generated, no need to write
      get() = field
      set(value) {
          field = value
      }
  //Can access like this:
  myCar.maxSpeed = 200 //set
  print("maxSpeed is: ${myCar.maxSpeed}") //get
  • Custom setter
1
2
3
4
  var maxSpeed : Int = 250
     set(value) {
        if (value > 0) value else throw IllegalArgumentException("Max speed cannot be less than 0")
    }
  • Private setter
    • getter's accessibility should be the same as the property's accessibility. (no need to explicitly make a private getter)
1
2
  var maxSpeed : Int = 250
     private set //cannot be accessed from outside of the class

6-5 Data class

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
  //Data class must have at least 1 primary constructor parameter
  //Data class cannot be abstract/open/sealed/inner classes
  data class User(val id: Long, var name: String)
  class Person(val id : Long, var name : String)

  fun main() {
      val user1 = User(1, "Wong")
      user1.name = "Li"
      val user2 = User(1, "Li")

      //Equality
      println(user1.equals(user2)) //true
      println(user1 == user2) //true
      println(user1 === user2) //false
      println(user1) //User(id=1, name=Li); called toString() method

      val person1 = Person(1,"Wong")
      val person2 = Person(1,"Wong")
      println(person1 == person2) //false
      println(person1.equals(person2)) //false
      println(person1 === person2) //false
      println(person1) //com.codelab.kotlinbasics.Person@3b6eb2ec

      //Copy
      val updatedUser = user1.copy(name = "Zhang")
      println(updatedUser) //User(id=1, name=Zhang)

      //Print component
      println(updatedUser.component1()) //print 1

      //Separate primary constructor parameters
      val (id, name) = updatedUser
      println("$id , $name") //1 , Zhang
  }

6-6 Inheritance

  • All classes and content are final by default, which means non heritable
  • Add open keyword to make a class heritable
  • ★Implemented Decorator pattern in Kotlin:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
  open abstract class Beverage {
      //properties
      open var description: String? = "Unknown Beverage"

      //methods
      abstract fun cost(): Double
  }

  /*-------------------------*/
  //components
  open class DarkRoast : Beverage() {
      init {
          super.description = "DarkRoast"
      }

      override fun cost(): Double {
          return 1.99
      }
  }

  /*-------------------------*/
  //Condiment Decorators
  open abstract class CondimentDecorator : Beverage() {
      lateinit var beverage: Beverage
      //properties can also be abstract or open
      //This is for compulsory inheritance(override)
      abstract override var description: String?
  }

  /*-------------------------*/
  //Decorators
  class Milk(beverage: Beverage) : CondimentDecorator() {
      init {
          super.beverage = beverage
      }

      override var description: String? = beverage.description + ", Milk"
      override fun cost(): Double {
          return 0.10 + super.beverage.cost()
      }
  }

  class Mocha(beverage: Beverage) : CondimentDecorator() {
      init {
          super.beverage = beverage
      }

      override var description: String? = beverage.description + ", Mocha"
      override fun cost(): Double {
          return 0.20 + super.beverage.cost()
      }
  }

  fun main() {
      var df: DecimalFormat = DecimalFormat("#.##")
      df.roundingMode = RoundingMode.DOWN

      //java: Beverage order = new DarkRoast()
      var order = DarkRoast() as Beverage
      order = Milk(order)
      order = Mocha(order)

      println(order.description + " $" + df.format(order.cost()))
      //DarkRoast, Milk, Mocha $2.29
  }

6-7 Interface

  • An interface is a contract that a class may choose to sign
  • Implementing an interface make a class obliged to implements its variables and functions
  • Interfaces can implements from other interfaces
  • Multiple implementation is allowed
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
  interface Runnable {
      var threadName: String?
      fun onDoingTask()
      fun onSuccess() : String
      fun onFailure() {
          println("Error occurred")
      }
  }

  //implements properties in primary constructor
  open class Task(override var threadName: String? = "ordinary worker") :Runnable {
      var isSucceed = true
      override fun onDoingTask() {
          if (isSucceed) onSuccess() else onFailure()
      }
      override fun onSuccess() : String {
          println("Succeed on thread: $threadName")
          return "OK"
      }
  }

  class BigTask : Task() {
      override var threadName: String? = "big worker"
      override fun onSuccess(): String {
          println("Succeed on thread: $threadName")
          return "Big OK"
      }

      override fun onFailure() {
          super.onFailure()
          println("on $threadName")
      }
  }

  fun main() {
      var myBigTask = BigTask()
      myBigTask.onDoingTask() //Succeed on thread: big worker(called method in child class)

      var myTask = Task()
      myTask.onDoingTask() //Succeed on thread: ordinary worker
}

6-8 Abstract class

  • You cannot create objects of an abstract class. However you can inherit subclasses from an abstract class
  • The members of an abstract class are non-abstract unless you explicitly use the abstract keyword
  • You don't need to annotate abstract classes or functions with open
  • You can override a non-abstract open member with an abstract one.
  • Different from interface:
    • Interface cannot hold states(fields, constructors..) but abstract classes can
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  abstract class Mammal(
      private val name: String, private val origin: String,
      private val weight: Double
  ) {
      //Non-abstract properties
      // Must be overridden
      abstract var maxSpeed: Double

      // Must be overridden
      abstract fun run()
      abstract fun breath()
  }

  class Human(override var maxSpeed: Double, name: String, origin: String, weight: Double) :
      Mammal(name, origin, weight) {
      override fun run() {
          //
      }

      override fun breath() {
          //
      }
  }

  fun main() {
      var laoWong = Human(30.0, "laoWong", "HZ", 80.0)
  }

6-9 Type casting

  • List
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
  val stringList: List<String> = listOf("laoWong", "laoChung", "laoChen")
  val mixedTypeList: List<Any> = listOf("laoWong", 123, 2.34, 'a')

  for(value in mixedTypeList) {
      when (value) {
          is Int -> println("Int value: $value")
          is Double -> println("Double value: $value")
          is Char -> println("Char value: $value")
          is String -> println("String value: $value")
          else -> println("Unknown type")
          //String value: laoWong
          //Int value: 123
          //Double value: 2.34
          //Char value: a
      }
  }
  • Smart cast
1
2
3
4
5
6
7
  val obj1: Any = "Word"
  if (obj1 !is String) {
      println("It's not a String")
  } else {
      //obj1 is automatically cast to a String in this scope
      println("Found a String od length ${obj1.length}")//Found a String od length 4
  }
  • Explicit unsafe casting using as
1
2
3
4
5
6
  val str1: String = obj1 as String
  println(str1.length)//4 (will work only when obj1 is s String)

  val obj2: Any = 123
  val str2: String? = obj2 as? String
  println(str2)//print null

7. Advanced

7-1 ArrayLists

  • used to create dynamic arrays
  • the size of an arrayList can be increased or decreased according to your requirement
  • provides both read and write functionalities
  • follows the sequence of insertion order
  • non synchronized, may contain duplicate elements
  • Constructors
    • ArrayList()
    • ArrayList(capacity: Int)
    • ArrayList(elements: Collection) (filled with the elements of a collection)
  • Functions
    • add(element: E)
    • clear()
    • get(index: Int)
    • remove(element: E)
  • Code
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
  //this 'val' means we can't reassign the object but we can change the content of it
  val list: List<String> = listOf("a", "b", "c", "d", "d")
  val arrayList = ArrayList(list) //fill with a collection(list)

  arrayList.add("e")
  arrayList.remove("b")
  for (i in arrayList) {
      var index = arrayList.indexOf(i)
      var value = arrayList[index]
      println("The element with index $index is: $i, \nshould be the same as $value")
  }
  arrayList.clear()

add list to arrayList

1
2
3
4
5
6
7
8
  val arrayList = ArrayList<String>(5)
  var list = mutableListOf<String>()

  list.add("1")
  list.add("2")

  arrayList.addAll(list)
  println(arrayList)//[1, 2]

Iterator

1
2
3
4
  var itr = arrayList.iterator()
  while(itr.hasNext()){
      print(itr.next()) //print 12 (using the same arrayList as above)
  }

7-2 Lambda expressions

  • Lambda is a function which has no name.
  • Lambda and anonymous functions are function literals(functions that are not declared, but passed immediately as an expression)
  • Lambda is defined with curly braces {} which takes variables as a parameter(if any) and a body of a function
  • the body of a function is written after the variable(is any) followed vy -> operator(lambda operator)
  • Syntax: { variable(s) -> body_of_lambda }
1
2
3
4
5
  val lambda: (Int, Int) -> Int = { a: Int, b: Int -> a + b }
  val lambda2 = { a: Int, b: Int -> println(a + b) }

  var result = lambda.invoke(3, 5)
  var result2 = lambda2(3, 5)

7-3 Visibility modifier

  • 4 types: public,private,protected,internal
    • public
      • Is accessible from everywhere in the project
      • It's a default modifier in kotlin.
      • All public declarations can be placed at the top of the file
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
      public class Example {
        class Demo{       
        }
    
        public fun hello()
        fun demo()
    
        public val x = 5
        val y = 10
      }
    
    • private
      • Allows the element to be accessible only within the block in which properties, fields, etc. are declared
      • A private package can be accessible within that specific file
    1
    2
    3
    4
    5
    
      private class Example { //Accessible within the same source file
        private val x = 1 //Accessible within class Example
        private doSomething() { //Accessible within class Example
        }
      }
    
    • Internal
      • Makes the field visible only inside the module in which it is implemented
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
      class Outer {
        var example = Example()//error: public example exposes this internal Example
                              //make example internal or Outer class internal will solve the problem
        internal class Example {
            internal val x = 5 //Basicly can't be accessed out of Outer class
            internal fun getValue() {
            }
        }
      }
    
    • protected
      • Allows visibility to its class or subclasses only
      • When overridden in subclasses is also protected unless it's explicitly changed
      • CANNOT be declared at top level -> packaged cannot be protected
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
      open class Base {
        protected val i = 0
        open protected val x = 5
      }
    
      class Derived: Base() {
        fun getValue: Int {
          return i
        }
        override val x = 6
      }
    

7-4 Nested and Inner class

  • Nested class: a class which is created inside another class
    • static by default
    • cannot access the data members of outer classes(one way accessibility)
  • Inner class: a class which is created inside another class with keyword inner
    • Nested class + inner mark => inner class
    • Cannot be declared inside interface or non-inner nested classes
    • Advantage over nested class: it's able to access members of its outer class even it is private
    • An inner class keeps an reference to its outer class
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
      fun main() {
          Outer().Inner().printOut()
      }
      class Outer {
          private val out = "out"
          inner class Inner {
              fun printOut() {
                  print(out)
              }
          }
      }
    

7-5 Safe cast and Unsafe cast operator

  • as? returns null if casting is not possible rather than throwing a ClassCastException (see 6-9 Type casting)

7-6 Exception handling

  • try-catch-finally
  • Unchecked Exception
    • Thrown due to mistakes in our code
    • Extends the RuntimeException class
      • ArithmeticException, SecurityException, ArrayOutOfBoundException, NPE...
  • Checked Exception
    • Checked at compile time
    • Extends Throwable class
      • IOException, SQLException...