Kotlin Grammars
Table of Contents
1. Basic Grammars
- Variables. Variables declared by
valis immutable, while declared byvaris mutable. - Function definition. Use the
funkeyword, all types are post - String Templates. Just like Python,
"${variable}"has the same meaning. - Null types. To let a variable be nullable, add
?after its type, eg.,val v : String? = null- Safe calls. suppose
a.b?.c?.d, if at any time, the propertyb,cdoes not exist, the function returnsnullinstead of raising error. - Elvis operator. If LHS is
null, this operator returns the RHS.nullString?.length ?: 0
- Safe calls. suppose
1.1. Basic Collections
List.Set, unordered and only stores unique items.- to create, use
setOf(xx, yy, zz)ormutableSetOf(xx, yy, zz) - to check existence, use
xx in set - to add or remove item, use
.add()and.remove()respectively
- to create, use
Map, stores key-value pairs.- to create, use
mapOf(k1 to v1, k2 to v2)ormutableMapOf(...)syntax - to access value through key, use
map[key]syntax. If the key does not exist, thennullis returned - to add items,
[]operator can also be used - to remove items, use
.remove(key)method - to check key existence, use
.containsKey() - to get collections of keys or values, use
.keysand.valuesrespectively
- to create, use
1.2. Control Flow
ifis similar to all PLswhenis similar toswitch - caseormatch - withflows as in other languages
val obj = "Hello"
when (obj) {
"1" -> println("One")
"Hello" -> println("Greeting")
else -> println("Unknown")
}
var result = when (obj) {
"1" -> "one"
else -> "unknown"
}
for(value in collection)just like other languages- Ranges.
1..4means1,2,3,4, while1..<4means1,2,3. To declare in reversed order, use4 downTo 1which is4,3,2,1. To declare with step, use1..5 step 2which is1,3,5.
- Ranges.
while()loop is same as other languages
1.3. Lambda Functions
Lambda functions are wrapped by {}, the syntax is
val function = { arg: Type -> /* expression */ }
2. Intermediate Kotlin Grammars
2.1. Extension Function
Usually, we can define methods inside the definition of a class.
class HttpClient {
fun request(method: String, url: String, headers: Map<String, String>): HttpResponse {
// ......
}
}
Sometimes, we want to add methods to some existing classes. For example, specification of get() and post() method:
fun HttpClient.get(url: String): HttpResponse = request("GET", url, emptyMap())
fun HttpClient.post(url: String): HttpResponse = request("POST", url, emptyMap())
The syntax is fun <receiver_class>.<method>(<args>): <return_type> = <body>. This style is also called extension-oriented design.
2.2. Scope Functions
In Kotlin, some scope functions allow us to create a temporary scope around an object and execute some code. Very similar to if let in Rust.
2.2.1. let function
The let function creates a scope that we can refer to the receiver with it keyword. Use the grammar sugar if we want to perform null checks and later perform further actions with that object.
val address: String? = ...;
val confirm = address?.let {
doSomething(it)
}
// which is equivalent to
val address: String? = ...;
val confirm = if (address != null) {
doSomething(address)
} else { null }
2.2.2. apply function
Use apply scope function to initialize objects, e.g., class instances, at the time of creation rather than later in the code.
val client = Client().apply {
token = "asdf"
connect()
authenticate()
}
fun main() {
client.getData()
}
2.2.3. run function
Similar to apply, but used when a result needs to be computed.
val client = Client().apply {
token = "asdf"
}
fun main() {
val result = client.run {
connect()
authenticate()
getData()
}
}
2.2.4. also function
Use also to complete an additional action with the object. This object is returned for further computation.
fun main() {
val medals: List<String> = listOf("Gold", "Silver", "Bronze")
val reversedLongUppercaseMedals: List<String> =
medals
.map { it.uppercase() }
.also { println(it) }
// [GOLD, SILVER, BRONZE]
.filter { it.length > 4 }
.also { println(it) }
// [SILVER, BRONZE]
.reversed()
println(reversedLongUppercaseMedals)
// [BRONZE, SILVER]
}
2.2.5. with function
Use with if we want to call multiple functions on the object.
val mainMonitorSecondaryBufferBackedCanvas = Canvas()
with(mainMonitorSecondaryBufferBackedCanvas) {
text(10, 10, "Foo")
rect(20, 30, 100, 50)
circ(40, 60, 25)
text(15, 45, "Hello")
rect(70, 80, 150, 100)
circ(90, 110, 40)
text(35, 55, "World")
rect(120, 140, 200, 75)
circ(160, 180, 55)
text(50, 70, "Kotlin")
}
2.3. Lambda expression with receivers
We can make lambda function accept a receiver to let it access member functions and properties. Its syntax is ClassName.[ ... ] where [ ... ] denotes the lambda function body.
2.4. Classes and Interfaces
By default, all classes are final, i.e., cannot be inherited. However, abstract classes can be inherited. An abstract class can have abstract property and abstract methods, whose implementation should be provided by subclasses, with explicit override keyword.
In Kotlin, each class can only have 1 parent, but can implement multiple interfaces. In short, abstract class uses “is-a” logic, while interfaces uses “can-do” logic.
2.4.1. Delegation
Suppose an interface DrawingTool and an already implemented class PenTool. Delegation is that when implementing CanvasTool, we directly reuse code from other implemented classes. Traditionally, if we want to do this, we have to declare each function
class CanvasSession(
val tool: DrawingTool
) : DrawingTool {
override fun draw() =
tool.draw()
override fun erase() =
tool.erase()
override fun getInfo() =
tool.getInfo()
}
With by keyword, we can get rid of duplicated code. The compiler will auto-generate code for us. Or we can override a few functions.
class CanvasSession(
val tool: DrawingTool
) : DrawingTool by tool
class CanvasSession(
val tool: Drawingtool
) : DrawingTool by tool {
override val color = "blue"
}
2.5. TODO Object
Kotlin’s built-in support for singletons.