Learn Kotlin by Example

Β· TechΒ· Java
Kotlin

[Learn Kotlin by Example](https://play.kotlinlang.org/byExample/overview?_gl=1*9t8fde*_gcl_au*MTMyMjIyOTg2NC4xNzI5Nz Q0OTky*_ga*MjIxOTA3MTMxLjE3Mjk3NDQ5ODg.*_ga_9J976DJZ68*MTcyOTc0NDk4OC4xLjEuMTcyOTc0NjM2OS41OC4wLjA.) All examples in this article are taken from the Kotlin doc. I did not organize all the contents, but added important contents excluding the basic contents such as Starting Kotlin below.

Kotlin types

Kotlin does not distinguish between primitive types and wrapper types. Basically, it is automatically converted to Java's primitive type and wrapper type at compile time without using the primitive type, that is, it is converted to the primitive type at compile time, and the converted bytecode is executed at runtime. The top type includes Any type and is used like Java's Object type. It is also a parent object of other Int and Boolean primitive types. The Unit type is marked as having no return, like void in Java, and there is no need to use an explicit return. You can see that there are no methods actually written and that they are created as singletons. You can see that the Nothing type is a function that does not terminate normally. Used as a type for functions that contain infinite loops or always throw exceptions. 🍎

infix function 🍎

It is a function that goes in between two objects and can be applied to the function using dispatcher, the object before the function, and receiver, the object after the function. Inside a class, dispatcher can be implemented by the class itself without having to define it separately.

infix fun Int.shl(x: Int): Int { ... }
1 shl 2
1.shl(2)

vararg parameters 🍎

Allows you to divide the given value based on a comma (,) and use it like an array.

fun printAll(vararg messages: String) {
for (m in messages) println(m)
}
printAll("Hello", "Hallo", "Salut", "Hola", "δ½ ε₯½")

Null safety

If you want to use null, when declaring a variable, you must use ? in the type to indicate that null can be entered as an argument. ? is a nullable type, and if you try to use null where it is not allowed, a compilation error will occur.

var nullable: String? = "You can keep a null here"

class

The default constructor is automatically generated in Kotlin. Unlike java, you do not need to indicate new when creating a default constructor.

Data class

Methods such as toString, copy, and componentN are automatically created, and to find an exact matching element, you can check whether hashCode matches.

println(user.hashCode())
Enum class

Enum can add the value of each property, and you can add a method to check whether there is a match using that property. 🍎

fun containsRed() = (this.rgb and 0xFF0000 != 0)
// 이런 λ°©μ‹μœΌλ‘œ λΉ¨κ°„μƒ‰μ˜ 포함여뢀λ₯Ό 확인할 수 μžˆλ‹€.
Sealed classes

This is a class used to limit inheritance.

sealed class Mammal(val name: String)                                                   // 1
class Cat(val catName: String) : Mammal(catName)                                        // 2
class Human(val humanName: String, val job: String) : Mammal(humanName)
fun greetMammal(mammal: Mammal): String {
when (mammal) {                                                                     // 3
is Human -> return "Hello ${mammal.name}; You're working as a ${mammal.job}"    // 4
is Cat -> return "Hello ${mammal.name}"                                         // 5
}                                                                                   // 6
}
  • Cast mammal to Human or Cat in when
  • Need to handle non-sealed classes

#####object 🍎 A lazy instance is an object that is used once, like a singleton in Java. The moment you call a function, an object object is created inside the function.

fun rentPrice(standardDays: Int, festivityDays: Int, specialDays: Int): Unit {
val dayRates = object {
var standard: Int = 30 * standardDays
var festivity: Int = 50 * festivityDays
var special: Int = 100 * specialDays
}
}

When declaring an object, unlike the expression above, there is a difference in that it cannot be used in the form of variable assignment and must be accessed immediately.

object DoAuth {
fun takeParams(username: String, password: String) {
println("input Auth parameters = $username:$password")
}
}

A companion object is used like static in Java, allowing it to be used globally. When used, it can be used as a package level function.

class BigBen {
companion object Bonger {
fun getBongs(nTimes: Int) {
for (i in 1 .. nTimes) {
print("BONG ")
}
}
}
}

Generics 🍎

E is an Element parameter that represents the generic type. E or any type can be used in this place. And the return refrain can also be done with E. Generic functions place <E> in front of the function name to make it clear what type is specified when calling the function.

fun <E> mutableStackOf(vararg elements: E) = MutableStack(*elements)

Inheritance

Use open to make a class inheritable.

Control Flow

when

In Kotlin, you can easily perform value-dependent processing within parentheses by using when instead of switch.

fun whenAssign(obj: Any): Any {
val result = when (obj) {
1 -> "one"
"Hello" -> 1
is Long -> false
else -> 42
}
return result
}
iterators 🍎

Provides functions such as next() and hasNext() as a way to consistently retrieve the values of collections (Set, List, Map).

class Zoo(val animals: List<Animal>) {
operator fun iterator(): Iterator<Animal> {             // 1
return animals.iterator()                           // 2
}
}
ranges

In loop statements such as for loops, you can use various expressions to determine the scope of repetition, as follows:

  • for(i in 0...3)
  • for(i in 0 until 3)
  • for(i in 2...8 step 2)
  • for(i in 3 downTo 0)
Equality Checks

In Kotlin, == returns true if the structural match is the same, and === returns true only if the element matches are identical. 🍎

val authors = setOf("Shakespeare", "Hemingway", "Twain")
val writers = setOf("Twain", "Shakespeare", "Hemingway")
println(authors == writers)   // 1
println(authors === writers)  // 2

Functional

A function can be used as a parameter or as a return function. In the code below, if you receive and use a function as ::sum in a function called calculate or calculate multiplication, you can perform the operation as specified in calculate.

val sumResult = calculate(4, 5, ::sum)
val mulResult = calculate(4, 5) { a, b -> a * b }

Since the operation returns a ::square function, 2*2 is performed.

fun operation(): (Int) -> Int {
return ::square
}
fun square(x: Int) = x * x
fun main() {
val func = operation()
println(func(2))
}

You can simply display a function using lambda as follows.

val upperCase6: (String) -> String = String::uppercase
Extension function

You can create extensible functions without inheritance or other design patterns. Extension properties are also supported. Even if you did not create a function in the class itself, you can define an extension function using . in the class without a new member.

fun Order.maxPricedItemValue(): Float = this.items.maxByOrNull { it.price }?.price ?: 0F

Collections

  • List is a collection that can only be read, and MutableList is a mutable collection.
  • Set is unordered and does not allow duplicates. MutableSet, like MutableList, is a mutable collection.
  • MutableMap is a mutable collection.
val EZPassAccounts: MutableMap<Int, Int> = mutableMapOf(1 to 100, 2 to 100, 3 to 100)
  • You can use filter to extract only those elements that meet a condition among the given object elements.
  • map allows you to operate on all elements of a given object.
  • any, all, and none all check whether all or none of the elements matching the condition exist in the given object and return a boolean value.
  • associateBy and groupBy create a map using collection elements with a specified key. 🍎
  • You can see in the results below that the output value is different depending on how the function is used. You can make only specific elements appear from an object, such as Person::phone, or you can make all elements centered around the value phone, like { it.phone }.
data class Person(val name: String, val city: String, val phone: String)
val people = listOf(
Person("John", "Boston", "+1-888-123456"),
Person("Sarah", "Munich", "+49-777-789123"),
Person("Svyatoslav", "Saint-Petersburg", "+7-999-456789"),
Person("Vasilisa", "Saint-Petersburg", "+7-999-123456"))
val phoneBook = people.associateBy { it.phone }
val cityBook = people.associateBy(Person::phone, Person::city)
val peopleCities = people.groupBy(Person::city, Person::name)
val lastPersonCity = people.associateBy(Person::city, Person::name)
// Result
People: [Person(name=John, city=Boston, phone=+1-888-123456), Person(name=Sarah, city=Munich, phone=+49-777-789123), Person(name=Svyatoslav, city=Saint-Petersburg, phone=+7-999-456789), Person(name=Vasilisa, city=Saint-Petersburg, phone=+7-999-123456)]
Phone book: {+1-888-123456=Person(name=John, city=Boston, phone=+1-888-123456), +49-777-789123=Person(name=Sarah, city=Munich, phone=+49-777-789123), +7-999-456789=Person(name=Svyatoslav, city=Saint-Petersburg, phone=+7-999-456789), +7-999-123456=Person(name=Vasilisa, city=Saint-Petersburg, phone=+7-999-123456)}
City book: {+1-888-123456=Boston, +49-777-789123=Munich, +7-999-456789=Saint-Petersburg, +7-999-123456=Saint-Petersburg}
People living in each city: {Boston=[John], Munich=[Sarah], Saint-Petersburg=[Svyatoslav, Vasilisa]}
Last person living in each city: {Boston=John, Munich=Sarah, Saint-Petersburg=Vasilisa}
  • When partition is used, the collection returns two lists (Pair<List<T>, List<T>) with elements that match the condition and values that do not.
val numbers = listOf(1, 2, 3, 4, 5) val (evenNumbers, oddNumbers) = numbers.partition { it % 2 == 0 }
  • flatmap functions to combine two collections into one. This is useful when combining corresponding lists in a nested structure into one list.
  • minOrNull, maxOrNull return the smallest or largest value in the collection, or null if that value is not found.
  • When using mapWithDefault, if there is no value, the length of the given key is returned as defined in the method below. 🍎
val mapWithDefault = map.withDefault { k -> k.length }
  • When two collections are combined using zip, Pair is applied to create one collection.
val A = listOf("a", "b", "c")
val B = listOf(1, 2, 3, 4)
val resultPairs = A zip B

Scope Functions

Nested scoped functions are not recommended.

  • You can check for null using let. When used with variable?.let{ ... }, let and below are executed when the variable is not null. Access objects using it.
  • run, another scoping function, uses this to access objects, unlike let. It is mainly useful when calling methods or properties of an object. It can be used naturally when working in the context of objects.
  • When accessing only member properties, you can also use with.
  • apply allows the use of code blocks within objects.
val jake = Person()
val stringDescription = jake.apply {
name = "Jake"
age = 30
about = "Android developer"
}.toString()
  • also is used like apply and makes it convenient to include additional actions.
val jake = Person("Jake", 30, "Android developer")
.also {
writeCreationLog(it)
}

Delegation

In Kotlin, class delegation patterns are possible using by.

class TomAraya(n:String): SoundBehavior by ScreamBehavior(n)

You can define properties by receiving elements of a Map structure by delegating properties.

class User(val map: Map<String, Any?>) {
val name: String by map
val age: Int     by map
}

Kotlin/JS

  • Used dynamically at runtime using dynamic.
  • Using JS function, you can use the alert function in Kotlin language like in JavaScript or create json.
val json = js("{}")
json.name = "Jane"
json.hobby = "movies"
println(JSON.stringify(json))
  • A function or property declared as external means that the actual implementation is provided in JavaScript. So in Kotlin there is no need to provide an implementation.
  • Supports HTML5 Canvas.
  • HTML creation is possible.
val result = html {
head {
title { +"HTML encoding with Kotlin" }
}
body {
h1 { +"HTML encoding with Kotlin" }
p {
+"this format can be used as an"
}
}
}

Comments

No comments yet. Be the first!

    164 posts in ν…Œν¬

    1–5 / 164