Mohsen Mirhoseini

@mohsenoid

My first experiences with Kotlin

some quick notes that might comes handy for beginners

Kotlin is a new language which at least most of Android developers heard about it or are using it instead of Java.

Out of my curiosity start learning it to know what’s attractive inside that makes some developer crazy about it! so during my learning process, I have made some notes which might look too minified but contains keywords for beginners and your later searches.

This is the notes which I prefer to have at the beginning of my learning process:

Variables:

val immutableName: String = "Mohsen"
var mutableName: String = "Mohsen"
var mutableName = "Mohsen"
var nullableName: String? = null
val numbers = arrayListOf(1, 2, 3, 4, 5)
print("Even Numbers are ${numbers.filter { i -> i % 2 == 0 }}")
val countries = listOf(Pair("Iran", "Tehran"), Pair("France", "Paris"), Pair("Germany", "Berlin"))
for ((country, city) in countries) {
print("Country: $country - City: $city")
}

Functions:

// fun stands for function!
fun addNumbers(first: Int, second: Int): Int {
return first + second
}
addNumbers(10, 20)
addNumbers(first = 10, second = 20)
addNumbers(second = 1, first = 2) // !!!
// you can have multiple params
fun addNumbers(vararg numbers: Int): Int {
return numbers.sum()
}
addNumbers(1, 2, 3, 4, 5, 6)

// default value
fun addTwoNumbers(first: Int, secound: Int = 0): Int {
return first + secound
}
addTwoNumbers(2)

Classes:

// no fields in classes! only properties and functions
class Customer(val id: Int = 0, var name: String) {
	// secondary constructor
constructor(id: Int) : this(id, "")

// initialize method
init() {
}

// public by default property
val test = 0

// visible inside Customer.kt
private fun foo() {}

// property is visible everywhere
// setter is visible only in Customer.kt
public var bar: Int = 5
private set

// visible inside the same module
internal val baz = 6
}

Data Classes:

// mutable data classes similar to AutoValue library which provide equals, toString, copy, etc.
data class Customer(val id: Int = 0, var name: String)

Extension Function:

fun String.convertSpacesToUnderscores(): String {
return this.replace(" ", "_")
}
print("This is my sample String.".convertSpacesToUnderscores())

Null:

val x: String = null //error!
val x: String? = null
// if x is not null range to 10
x?.rangeTo(10)

Let:

var customer: Customer? = null
customer?.let {
// it = user and not null and only read once!
println(it.name)
}
someMethod().let { result ->
//method only run once!
}

Any:

fun getStringLen(obj: Any): Int? = if (obj is String) obj.length else null

Companion:

// the same as Static methods and you have to access the
// method like this: Customer.Companion.what()
class Customer {
companion object {
fun what(customer: Customer): String {
return customer.id.toString() + customer.name
}
}
}
// If you want the function looks like java static methods
// like this: Customer.what() have to add JvmStatic
// It is useful for calling static methods from NDK libraries
class Customer {
companion object {
@JvmStatic
fun what(customer: Customer): String {
return customer.id.toString() + customer.name
}
}
}

Package function:

// functions with no classes, just the same as java import static
package com.google.test
fun packageFunction() {
println("I am a package function!!")
}

List, ArrayList, Map:

val list = listOf("a", "b")
val mutableList = mutableListOf("a", "b")
for (s in list) {
// ...
}
val map = mapOf("key" to "value")
val mutableMap = mutableMapOf("key" to "value")
for ((key, value) in map) {
// ...
}

Interfaces:

interface TestInterface {
fun run(t:Int): String

fun test(){
// default body!
}
}

Generic:

class Box<T>(t: T) {
// ...
}
Val box: Box(1) // box: Box<Int>

Enums:

enum class Direction(val degree: Int) {
NORTH(0), EAST(90), SOUTH(180), WEST(270)
}

Type Casting:

val y = x as Int
val y = x as? Int // nullable
// smart cast
If (x is String)
x.toUpperCase()

Operator Overloading:

operator fun plus(other: Point): Point = Point(x + other.x, y + other.y)

Inheritance:

// all classes are final by default and for inheritance should be open
open class Shape {
}
class Circle: Shape() {
}

infix:

infix fun String.hello(name:String): String{
return "Hello, $name. $this"
}
"Test" hello "test" // == "Test".hello("test")

Delegates:

interface Repository<T : Record> {
fun getById(id: Int): T
fun getAll(): List<T>
}
class Controller(repository: Repository<Customer>) : Repository<Customer> by repository {
fun customerList(): List<Customer> {
return getAll()
}

fun customerById(id: Int): Customer {
return getById(id)
}
}
var test: String by Delegates.observable("") {
prop, old, new ->
println("$old -> $new")
}

Sealed:

// sealed means there is no PageResult child more than ones that are here in this file
sealed class PageResult
class Success(val url: String) : PageResult()
class Error(val code: Int, val message: String) : PageResult()
fun getPage(url: String): PageResult {
val someOperationIsSuccessfull = false

if (someOperationIsSuccessfull)
return Success(url)
else
return Error(404, "Not found")
}
fun callingPage() {
val result = getPage("http://google.com")
when (result) {
is Success -> println(result.url)
is Error -> println(result.message)
}
}

Lazy:

class User(firstName: String, lastName: String) {
val fullName: String by lazy { "$firstName $lastName" }
	fun printFullName() {
println("User full name is: $fullName")
}
}

Local return:

fun myFunction() {
val numbers = 1..100
numbers.forEach {
if
(it%5==0)
return@forEach
}

println("Hello!!") //Hello will be printed!
}

Tailrec:

//tailrec convert a recursive function call to a goto loop
tailrec fun
factorial(number: Int, accumulator: Int = 1): Int {
when (number) {
0 -> return accumulator
else -> return factorial(number - 1, accumulator * number)
}
}

Some unusual syntaxes for Java developers!!:

// multiline string literals
print("""1. test
2. unit test
3. what a test""")
print("""1. test
2. unit test
3. what a test""".trimIndent())
print("""|1. test
|2. unit test
|3. what a test""".trimMargin())
fun max(a: Int, b: Int) = if (a > b) a else b
// no for (int i=0;i<10;I++) loop!!!
val myArray = arrayOf("A","B","C","D")
for (index in 0 until myArray.size){
println(myArray.get(index))
}
for ( i in 1..10 )
println(myArray.get(index))
}
for ( i in 100 downTo 1 step 5)
println(myArray.get(index))
}
var i = 0
while (i < 10)
println (i++)
// no switch case default
when (i) {
0 -> println("zero")
1..5 -> println("number")
is String -> println("$i is string")
!is String -> println("is not string")
else -> println("unknown!!!")
}
val isString = when ("Hello") {
is String -> true
else -> false
}
val isInRange = i in 5..9
val isMember = "hello" in array
// set alias while importing!
Import com.google.Test as MyTest
// loop operators
while (true){
if(false) break
if(false) continue
if(false) return
}
// labels
outer@ for (l in list) {
for (m in map) {
if (l == m.key) break@outer
}
}
// Pair and Triple data types
val myPair = Pair(1, "Mohse")
val myTriple = Triple(1, "A", "Mohsen")
val (id, name) = myPair
// measure runtime in ms!!
val took = measureTimeMillis {
// ...
}
// deprecate a method
@Deprecated ("Use another method!", level = WARNING/ERROR/HIDDEN, replaceWith = ReplaceWith("newPrintFullName()", imports = "com.google.test"))
fun printFullName() {
// …
}
// rename method for Java
@JvmName("methodNameInJava")
fun printFullName() {
// …
}
// inject using qualifier annotation
@field:[Inject ApplicationContext]
lateinit var context: Context
// exclude field from serialization and gson
@Transient
var id: String? = null

Kotlin Android Extensions:

// add plugin to build.gradle
apply plugin: 'kotlin-android-extensions'
import kotlinx.android.synthetic.main.<layout>.*
// access views without findViewById!!
myText.text = "Hello world!"

I look forward for your comments, experiences with Kotlin, and any unusual syntaxes which I might miss as a beginner. Share this article if you think it is useful by tapping 💚 button, and follow me for more articles Mohsen Mirhoseini.

More by Mohsen Mirhoseini

Topics of interest

More Related Stories