Shortcuts
Basics
Kotlin REPL
$ kotlinc-jvm
Exit the REPL:
:quit
print("Hello world!")
println("HEllo world!")
Variable declaration
// Mutable variable. Value can be changed
var name : String = "James"
// Immutable value. Similar to Java final
val countryCode : String = "MYR"
Nullable variable
var name: String? = null
name = "Hello"
Check for null
var name: String? = "Hello World"
name?.toUpperCase()
name?.let {
print(it.length)
print(it.toUpperCase())
}
String interpolation
val name: String = "Martin"
print("Hello $name. Welcome to Kotlin")
Multiple line string
val longText = """
This is
an example
of a very
long text
in multiple line
"""
// Mult line with formatting
val longText = """
| This is
| an example
| of a very
| long text
| in multiple line
""".trimMargin()
Ternary operation
val randomNumber : Int = 11
val isGreaterThanTen : Boolean = if(randomNumber > 10) true else false
Bitwise operations
val x : Boolean = true
val y : Boolean = false
x and y // false
x or y // true
x xor y // true
32 shr 1 // shift right 16
32 shl 2 // shift left 128
-32 ushr 2 // shift right unsigned 1073741816
Type checking
interface Vehicle {
fun getPlateNumber(): String
}
class Car(private val number: String, val seatNumber: Int) : Vehicle {
override fun getPlateNumber(): String {
return number
}
}
val randomVehicle : Vehicle = Car("ABX", 4)
println(randomVehicle.getPlateNumber())
if(randomVehicle is Car) {
val car : Car = randomVehicle
println(car.seatNumber)
}
Range condition
val number : Int = 100
if(number in 1..100) {
println("Number is between 1 to 100")
}
When operation
val number : Int = 55
when(number) {
in Int.MIN_VALUE .. -1 -> println("It's negative")
0 -> println("It's zero")
in 1..9 -> println("It's single positive digit")
in 10..99 -> println("It's double digit")
100 -> println("It's hundred")
else -> println("Something else!")
}
// Other form
when {
number < 0 -> println("It's negative")
number == 0 -> println("It's zero")
number in 1..9 -> println("It's single positive digit")
number in 10..99 -> println("It's double digit")
number == 100 -> println("It's hundred")
else -> println("Something else!")
}
Looping
val x = 10
// print 1 to 10
for(i in 1..10) {
println(i)
}
// print 1 to 9
for(i in 1 until 10) {
println(i)
}
// print 1, 3, 5, 7, 9
for(i in 1..10 step 2) {
println(i)
}
// print 10 to 1
for(i in 10 downTo 1) {
println(i)
}
// print 10, 8, 6, 4, 2
for(i in 10 downTo 1 step 2) {
println(i)
}
val languages : Map<String, String> = mapOf("EN" to "Hello", "DE" to "Hallo", "FA" to "Salam")
languages.forEach{ (languageCode, greeting) ->
println("Language: $languageCode - Greeting: $greeting")
}
for((languageCode, greeting) in languages) {
println("Language: $languageCode - Greeting: $greeting")
}
val myList : List<Int> = listOf(1, 10, 3, -500, 5, -1, 34, 44, -2)
myList.filter { it > 0 }.sorted().forEach { println(it) }
Collections
Arrays
val arr = arrayOf("x", "y", "z")
println(arr[0])
val (key, value) = "EN=Hello".split("=")
println("Language is $key, message is $value")
List
val lst : List<String> = listOf("One", "Two", "Three")
Map
val mp : Map<String, Int> = mapOf("One" to 1, "Two" to 2, "Three" to 3)
mp.forEach { (k, v) -> println("$k = $v")}
Methods
fun sum(val x: Int, val y: Int) : Int {
return x + y
}
fun sum(val x: Int, val y: Int) : Int = return x + y
// varargs
fun sum(vararg numbers: Int) : Int {
return numbers.sum()
}
Classes
Java-like class
/* Class with empty constructor */
class Book {
var name : String = ""
var author : String = ""
var isbn : String = ""
}
Class with all constructor values
val book : Book = Book()
book.name = "12 rules of life"
book.author = "Jordan Peterson"
book.isbn = "1234ABC"
/* Class with all constructor values */
class Book(val name: String, private val author: String, val isbn: String) {
fun printAuthorName() {
println(author)
}
fun getAuthorName(): String = author
}
val book = Book("12 rules for life", "Jordan Peterson", "ABC")
book.printAuthorName()
// book.author -> doesn't work since author is private
book.getAuthorName()
Class with default value in constructor
class Book(val name: String, private val author: String, val isbn: String = "DEFAULT ISBN") {
fun printAuthorName() {
println(author)
}
fun getAuthorName(): String = author
}
// We do not pass ISBN, uses 'DEFAULT ISBN'
val book = Book("12 rules for life", "Jordan Peterson")
// We pass all values
val bookWithIsnb = Book("12 rules for life", "Jordan Peterson", "ABCD")
Class with lazy value initialization
class Book(val name: String, private val author: String, var isbn: String = "DEFAULT ISBN") {
fun printAuthorName() {
println(author)
}
fun getAuthorName(): String = author
}
}
Companion object
class Book(val name: String, private val author: String, var isbn: String = DEFAULT_ISBN) {
companion object {
const val DEFAULT_ISBN : String = "DEFAULT ISBN"
val logger : Logger = LoggerFactory.getLogger(Book.javaClass)
}
fun printAuthorName() {
logger.info("Author is $author")
println(author)
}
fun getAuthorName(): String = author
}
Utility class with Java like static methods
class SomeUtility {
private constructor()
companion object {
fun doSomething() {
}
fun doOtherThing() {
}
}
}
Class with custom getter and setter
class Book(val name: String, private val author: String, var isbn: String = DEFAULT_ISBN) {
private var nameA: String = ""
set(value) {
if (!value.isNotEmpty()) {
throw IllegalArgumentException("Title must not be empty")
}
field = value
}
get() {
return field
}
companion object {
const val DEFAULT_ISBN: String = "DEFAULT ISBN"
val logger: Logger = LoggerFactory.getLogger(Book.javaClass)
}
}
Primary, secondary constructors
class Car(val name: String, val plateNo: String) {
var new: Boolean? = null
var colour: String = ""
constructor(name: String, plateNo: String, new: Boolean) : this(name, plateNo) {
this.new = new
}
constructor(name: String, plateNo: String, new: Boolean, colour: String ) :
this(name, plateNo, new) {
this.colour = colour
}
}
// directly calls primary constructor
val car1 = Car("Peugeot 504", "XYZ234")
// directly calls 1st sec. constructor
val car2 = Car("Peugeot 504", "XYZ234", false)
// directly calls last sec. constructor
val car3 = Car("Peugeot 504", "XYZ234", false, "grey")
Adding functionalities to class without inheriting
fun Int.triple(): Int {
return this * 3
}
var result = 3.triple()
Late initialization
It is used when we want to postpone the object initialization to another stage.
internal lateinit var person: Person
And somewhere else in the code:
person = Person()
It’s only applicable to non-null var
.
Lazy initialization
It’s used when we want to defer the calling to an expensive call. For instance, hitting the database. It’s the same concept as lazy loading. Means the object is not initialized until it’s accessed for the first time.
private val bookService = BookService()
val books : List<Book> by lazy {
bookService.getAllBook()
}
It’s only applicable to val
.
Try resource (with use)
val file = File("test.csv")
file.bufferedReader().use {
println(it.readText())
}
Immutable list to mutable
listOf("One", "Two", "Three").toMutableList().add("Four")
Any, Unit, and Nothing
Any
equals to JavaObject
Unit
equals to JavaVoid
Nothing
indicates a method never terminates normally (meaning it throws exception)
Also, let, with, run
Basically they all do similar thing but only differnt in how the context object is references and returned output.
apply
, run
, with
access context object with this
whereas also
or let
access context object with it
.
apply
, also
return itself (original object) whereas run
, with
, let
return result.
@Test
fun `let vs also vs apply vs with vs run`() {
val str : String = "test"
var strUpper = str.let { it -> it.toUpperCase() }
assertThat(strUpper).isEqualTo("TEST")
strUpper = str.also { it -> it.toUpperCase() }
assertThat(strUpper).isEqualTo("test")
strUpper = str.run { this.toUpperCase() }
assertThat(strUpper).isEqualTo("TEST")
// with `with` we can access members of object
strUpper = with(str) {
println(length)
this.toUpperCase() // or just `toUpperCase()`
}
assertThat(strUpper).isEqualTo("TEST")
strUpper = str.apply { this.toUpperCase() }
assertThat(strUpper).isEqualTo("test")
// let can be used for null check
val str2 : String? = null
str2?.let {
println("$it is not null")
}
}
Print a string character by character
val st = "Hello World!"
for (ch in st) {
println(ch)
}
for (i in 0 until st.length) {
println(st[i])
}
// Print 0 to 10 (inclusive)
for (i in 0..10) {
println(i)
}
IntArray
val x : IntArray = IntArray(10) // Size
// initialize with value
var x : IntArray = intArrayOf(10, 20, 30)