lessons - isel-leic-tds/2223i.LI32D Wiki

Lessons:


Lesson 01 - Introduction and Kotlin Characteristics

  • Characterizing Kotlin programming environment:
    • Statically typed
    • For JVM and for native and cross-platform
    • Multiparadigm: imperative, object-oriented and functional.
  • Programming languages retrospective and comparison with Kotlin.
  • Since functional programming languages to object oriented, Lisp, Java, JavaScript, C#, Scala, etc
  • Software and source code quality criteria: Reliability, Testability, Readability and Extensibility.
  • Lessons approach:
    • Git repository
    • Multi-module project (one module per lesson) with gradle wrapper (version 7.5.1)
    • Kotlin release 1.6....
    • IntelliJ 2022
    • Homework workouts

Lesson 02 - Encapsulation and Limit Mutability

  • Interface as a contract (e.g. Stack<T>) with abstract member functions
  • Abstract classes => cannot be instantiated
  • Instantiate => create an instance of a class
  • Object's state => values of the object's properties

  • Restricting and managing access to object’s state (i.e. instance properties or fields).
  • Hide properties inside the class (private)
  • Provide methods/functions to access object’s state:
    • e.g. NaifDate member functions nextMonth() and addDays(inc)

  • Limit Mutability
  • Kotlin mutable properties (var) versus immutable (val)
  • Implement Stack interface based on auxiliary class Node<T>(val item: T, val next: Node<T>?) with the expected behavior:
interface Stack<T> {
  fun push(item: T)
  fun peek(): T
  fun isEmpty(): Boolean
  fun pop(): T
}
fun main() {
  val stk = StackImpl<String>()
  stk.push("ISEL")
  stk.push("TDS")
  println(stk.peek()) // TDS
  while( !stk.isEmpty() ) println( stk.pop() ) // TDS ISEL
  stk.peek() // throws NoSuchElementException
}


Lesson 03 - Unit Testing

  • "isolate each part of the program and show that the individual parts are correct"
  • E.g. refactoring the former main() in individual unit tests:
    • new instance of Stack should be empty
    • last pushed item is the peeked item
    • after pop of a Stack with single item it stays empty
    • pop an empty stack throws NoSuchElementException
    • peek an empty stack throws NoSuchElementException
  • Unit testing frameworks (e.g. JUnit, TestNG, etc) provide:
    • Strict, written contract, through annotations (e.g. @Test, @Expect, etc).
    • API to express and evaluate the expected behavior (i.e. assertions).
    • Automated runtime to scan, perform and collect tests results.
  • E.g. kotlin.test. API:
    • assertEquals(expected, actual)
    • assertTrue(condition)
    • assertFailsWith<ExceptionClass> { ... /* block */ ... }

Lesson 04 - Types

  • Limit Mutability, E.g. Implementation of an immutable FixedStack
  • Implementation of an immutable NaifDate
  • Classes and data classes
  • Classes may have a primary constructor and one or more secondary constructors.
  • The primary constructor is a part of the class header.
  • Initialization code can be placed in initializer blocks prefixed with the init keyword.

Lesson 05 - Types

  • Any - the root of the Kotlin class hierarchy
  • Canonical functions: equals, hashCode e toString
  • Modifiers: abstract, open, and override
  • E.g. override fun toString(): String = "$day-$month-$year"
  • Classes and data classes (provides implementation of canonical methods based on its properties)
  • Inspecting resulting data classes with javap (e.g. toString() and equals())
  • Equality - Structural equality (== a check for equals())
  • Referential equality (=== two references point to the same object)
  • Properties with custom accessors - called every time you access the property (this way you can implement a computed property)
  • Property versus Custom getter:
    • val nextMonth = month % 12 + 1 => private final int nestMonth; (JVM field)
    • val nextMonth get() = month % 12 + 1 => private final int getNextMonth(); (JVM function)

Lesson 06 - Enumerated Types

  • Implement a prefix calculator for integer expressions.
  • Object oriented approach => interface IntExpr { fun eval() : Int }
    • IntExpr <|----- IntExprLiteral
    • IntExpr <|----- IntExprOperator
  • v1 – without typed operator - Operator is a Char
  • v2 – with a typed operator (i.e. Strongly Typed)
  • Enumerated Type (e.g. Kotlin enum class):
    • A data type with a set of named values, i.e. constants (e.g. SUM, DIV, etc)
    • Each constant is an instance of its enumerated type (e.g. Operator)
  • Companion Object - its members can be called simply by using the class name as the qualifier (e.g. Operator.parse(...))
  • E.g.
enum class Operator(private val opr: Char) {
  ...
  companion object {
      fun parse(opr: Char) : Operator {
          return values().find { it.opr == opr } ?: throw IllegalArgumentException()
      }
  }
}

Lesson 07 - Polymorphism versus Pattern Matching

  • Object declarations - object - singleton pattern.
  • Operator overloading, e.g. binary operations
  • Association ---> versus Inheritance -----|>
  • UML classes diagrams.
  • E.g. NaifDate with Month and IntExprOperator with Operator
  • Sealed classes and interfaces represent restricted class hierarchies that provide more control over inheritance
  • sealed versus open
  • Implementing functional approach for IntExpr
  • Dynamic dispatch (polymorphism) versus Pattern matching
  • E.g. dynamic dispatch - invokeinterface
  • E.g. pattern matching
fun IntExpr.eval(): Int = when(this) {
    is IntExprLiteral -> nr
    is IntExprOperator -> opr.eval(a.eval(), b.eval())
}

Lesson 08 - Domain model

  • Structuring modules to build a TicTacToe game for UI console and GUI (graphical user interface):
    1. tictactoe-model
    2. ui – console single-device
    3. storage - persistence in Document Database - NoSQL | MongoDB
    4. ui – console between devices
    5. gui - graphical user interface
  • tictactoe-model: Player, Position, Move and Board
  • Control illegal arguments and states:
    • IllegalArgumentException and IllegalStateException

Lesson 09 - Manage instances and states

  • runtime checks — require() and check()
    • IllegalArgumentException and IllegalStateException
  • Manage Position instances
  • private constructor
  • Make Position(..., ...) forward to:
    • companion object { operator fun invoke(l: Int, c: Int): Position {...}}
  • Parentheses are translated to calls to invoke with appropriate number of arguments.
  • Manage invalid states
  • Type checks and casts
  • "Unsafe" cast operator, E.g. (board as BoardWin).winner
⚠️ **GitHub.com Fallback** ⚠️