LongReian: Kotlin Ep. 1

A’phirat Nimanussonkul
5 min readJun 15, 2020

--

ลองเรียนภาษา Kotlin กันเถอะ: Variable, Collection, Function, Control Flow

LongReian

หรือ ลองเรียน เป็น Series ที่ผมจะจดบันทึกข้อมูลและขั้นตอนการเรียนรู้ด้วยตัวเองของผม เพื่อช่วยบันทึกความจำ และเป็นคลังความรู้ครับ อีกทั้งอาจจะเป็น Series ให้กับคนที่สนใจการเรียนรู้หัวข้อนั้นๆ ได้ศึกษาไปพร้อมๆกับผมได้ด้วย เหมือนเรามีเพื่อนเรียนไปด้วยกัน ทดลองเรียนรู้ไปด้วยกัน หวังว่าจะเป็นประโยชน์กับทุกท่านที่ได้เข้ามาอ่านนะครับ

Kotlin เป็นภาษาที่เกิดมาเพื่อใช้เขียน Mobile Application ของ OS Android และตัวผมก็อยากฝึกเขียน Android เพื่อพัฒนาความรู้ความสามารถของตัวเอง แต่พื้นฐานทางภาษา Syntax Kotlin ยังไม่มี (พอไปดูคลิปที่สอนเขียน mobile app แล้วตามไม่ทัน) จึงมองย้อนกลับไปยังพื้นฐานที่ตัวเองมี ยังไม่แน่นพอ

เคยเป็นไหมเวลาที่เราดูคลิปสอน ณ ตอนนั้นมันเข้าใจมากๆ จำได้เลย แต่พอให้หลังไป 1 วัน กลับลืมสะได้ ใช่ครับทุกคนเป็นหมด ดังนั้นเพื่อทบทวนตัวเองอยู่เสมอๆ ผมจึงได้เขียนบทความนี้ขึ้นมา เพื่อจดและทบทวนเนื้อหาความรู้ที่ตัวเองได้เรียน อาจจะขาดตกบกพร่องตรงไหนไปบ้าง หรือข้อมูลผิดบ้าง สามารถแนะนำได้ครับ เพราะผมก็ Kotlin Beginner เลยละครับ

Variable

การประกาศตัวแปร

การประกาศตัวแปรในภาษา Kotlin มีสองแบบหลักๆ โดยใช้ val, var

  • var เป็นการประกาศตัวแปรที่สามารถแก้ไขค่าในตัวแปรได้ภายหลังจากที่ประกาศ
  • val (Read only)เป็นการประกาศตัวแปรที่ไม่สามารถแก้ไขค่าได้ ค่าที่ตัวแปรนั้นเก็บไว้คือค่าที่กำหนดให้ตอนประกาศตัวแปร
//Mutable สามารถเปลี่ยนแปลงค่าได้ภายหลัง
var name = "Aphirat"
//Immutable ไม่สามารถเปลี่ยนแปลงค่าได้
val greeting = "Hello"

ระบุประเภทตัวแปร

จากตัวอย่างด้านบนเป็นการประกาศตัวแปรแบบไม่มีประเภทตัวแปรกำกับ Kotlin จะรู้เองว่าตัวแปรนั้นควรเป็นประเภทอะไร โดยดูจากค่าที่เรา assign ให้

แต่ถ้าหากกำหนดประเภทตัวแปรก็ทำได้ (สำหรับผมคิดว่าประกำหนดประเภทตัวแปรไปด้วยก็ดี เพราะคนอื่นที่มาอ่านจะได้เข้าใจได้ง่ายขึ้น)

var name: String = "Aphirat
val greeting: String = "Aphirat"

Nullable

ตัวแปรทุกประเภทใน Kotlin จะมีค่าเริ่มต้นเสมอ และไม่ใช่ null ดังนั้นเราไม่สามารถ assign ค่า null ให้ได้ แต่ถึงกระนั้นในความเป็นจริง เราไม่ทางรู้ได้เลยว่าข้อมูลจะมีหรือไม่มี จึงเกิดตัวแปรที่สามารถมีค่า null ได้ (nullable)

//Nullable
var name: String? = null

การประกาศตัวแปรที่เป็น nullable จะเติม ‘?’ ตรงท้ายประเภทตัวแปร ทำให้สามารถ assign ค่าที่เป็น null ได้

Safe Call

เมื่อตัวแปรสามารถเป็น null ได้ การใช้งานก็ต้องระวังเป็นอย่างดีไม่งั้นอาจจะเกิด error ได้ ดังนั้นการเรียกตัวแปร nullable ทุกครั้งจะใช้เครื่องหมาย ‘?’ ต่อท้าย เพื่อเป็นการบอกว่าตัวแปรที่เราเรียกใช้นี้อาจจะมีค่าหรือไม่มีก็ได้ ถ้าหากไม่มีก็จะไม่ทำงาน

var name: String = "Aphirat"
println(name.length)
var greeting: String? = null
println(name?.length)

นอกจากนี้เรายังสามารถใช้ let เพื่อเป็นช่องทางหนึ่งในการตรวจสอบค่าว่าเป็น null หรือไม่ พร้อมทั้งสามารถเขียนโค้ดลงไปถ้าเงื่อนไขเป็นจริง (ค่าไม่เป็น null) โดยไม่ต้องใช้ if

var name: String? = null
//เขียนเยอะไป
if(name != null) {
println("It's not null")
} else {
println("It's null")
}
//ใช้ let
name?.let { println("It's not null") } ?: println("It's null")
//output -> "It's null"

Tip
การใช้ let ต้องใช้คู่กับเครื่องหมาย ‘?’ ในกรณีที่เป็นตัวแปร nullalble ไม่เช่นนั้นผลลัพธ์จะได้แค่ “It’s not null” เท่านั้น เพราะถูกมองว่าไม่มีทางเป็น null

Elvis Operator

จากโค้ดด้านบนที่ใช้ let ตรวจสอบเงื่อนไขแทนที่จะใช้ if และใช้เครื่องหมาย ‘?:’ แทนที่จะใช้ else เครื่องหมาย ‘?:’ นั่นเองเรียกว่า elvis operator

var name: String? = null
var nameLength = name?.length ?: -1
//nameLength = -1

จากตัวอย่างด้านบนนั้นเป็นการกำหนดค่าโดยนำความยาวตัวอักษรตัวแปร name มาใส่ให้ตัวแปร nameLength ถ้าตัวแปร name ไม่ใช่ null แต่ถ้าอยากให้กำหนดค่าอื่นๆได้ สามารถใช้ elvis operator กับ let ได้ ดังนี้

var name: String? = "Aphirat"
var nameLength = name?.length?.let { 20 } ?: -1
//nameLength = 20

!! Operator

ในบางโอกาสที่เราใช้ฟังก์ชั่นที่ต้องการค่าที่ไม่ใช่ null นั้น ถ้าหากเราส่งค่าที่เป็น nullable เข้าไป โปรแกรมก็จะขึ้นฟ้องว่าฟังก์ชั่นนี้รับเฉพาะค่าที่ไม่ใช่ nullable นะ เราจะใช้เครื่องหมาย ‘!!’ เพื่อแปลงตัวแปรให้เป็น not-nullable

var name: String? = "Aphirat"
var nameLength = name!!.length
//nameLength = 7

String Concat

เดิมภาษา Java จะต้อง string โดยใช้เครื่องหมาย ‘+’ หรือใน JavaScript จะใช้ ‘${}’

// JS
let str = "String concat"
console.log(`${str} in js`) //String concat in js
or
console.log(str + " in js") //String concat in js

kotlin เองก็สามารถต่อ string ได้คล้ายกันกับ JS เลย เช่น

//Kotlin
var name: String = "Aphirat"
println("My name is " + name) //My name is Aphirat
orprintln("My name is $name") //My name is Aphirat//ถ้าต้องการแทรกค่าลงไป โดยไม่ใช้ตัวแปรตรงๆ แต่เป็น expression หรือเรียกฟังก์ชั่นอะไรก่อนจะต้องเพิ่ม {} เข้าไปด้วยเหมือน JSorprintln("Your name length is ${ name.length }")
//Your name length is 7

Function

การเขียนฟังก์ชั่นใน Kotlin ไม่มีอะไรแตกต่างกับภาษาอื่นมากนัก ประกาศโดยใช้ fun <function_name>() {}

การรับค่าที่เข้ามาจะรับมาพร้อมกับชื่อและประเภทตัวแปร และการ return ก็จะบอกประเภทตัวแปรที่จะ return กลับไปด้วย ทำให้โปรแกรมรู้สึกอ่านง่ายขึ้นมา เพราะมีประเภทตัวแปรกำกับให้

แต่ถ้าฟังก์ชั่นไหนไม่มีการ return ค่า ก็ไม่ต้องบอกประเภทตัวแปร

fun main() {
...
}
fun sum(num1: Int, num2: Int): Int {
return num1 + num2
}

จากตัวอย่างฟังก์ชั่น sum ที่ทำงานเพียงอย่างเดียวไม่ซับซ้อน สามารถลดรูปฟังก์ชั่นให้สั้นลงได้

fun sum(num1: Int, num2: Int) = num1 + num2orfun sum(num1: Int, num2: Int): Int = num1 + num2orfun printText(text1: String, text2: String) = println("$text1 $text2")

Tip
ประเภทตัวแปรที่ return จะบอกหรือไม่บอกก็ได้ ถ้าเขียนฟังก์ชั่นในลักษณะนี้

Parameter values

ฟังก์ชั่นที่ต้องรับ parameters เมื่อเราเรียกก็ต้องส่งไป และฟังก์ชั่นนั้นก็จะทำการ map ค่าที่ส่งเข้ามากับตัวแปรที่ฟังก์ชั่นประกาศไว้

โดยปกติถ้า paramters มากกว่า 1 เราจะต้องส่งไปตามลำดับที่ฟังก์ชั่นนั้นประกาศรับไว้ แต่ใน Kotlin เราสามารถกำหนดได้ว่าค่าที่เราจะส่งไปในตำแหน่งนี้ จะให้ map เข้ากับตัวแปรไหนในฟังก์ชั่น

fun printText(text1: String, num1: Int) = println("$text1 $num1")//call
printText(num1 = 5, text1 = "Hello")
//Hello 5

Parameters default value

สามารถกำหนดค่าเริ่มต้นให้กับ parameters ที่ฟังก์ชั่นได้ ซึ่งถ้ามีการเรียกฟังก์ชั่นที่สร้าง default value แล้ว ไม่จำเป็นต้องส่งค่าเข้ามาก็ได้ ฟังก์ชั่นจะใช้ค่าเริ่มต้นนั้นแทน แต่ถ้าส่งมา ก็จะใช้ค่าที่รับเข้ามา

fun printText(text1: String = "Hello", num1: Int = 20) = println("$text1 $num1")//call without parameters
printText()
//Hello 20
//call with some parameters
printText("Sawaddee")
//Sawaddee 20

Control Flow

ประกอบไปด้วย loop ชนิดต่างๆ ได้แก่ for, while, do while และการตรวจสอบเงื่อนไข if else, when

For loops

การเขียน for loop ใน Kotlin จะใช้ (for … in …) คล้ายกับการ forEach สามารถเขียนได้โดย

for(course in courses) { ... }// การกำหนดประเภทตัวแปรที่ for loop
for(number: Int in listOfNumber { ... }
// for loop จากช่วงตัวเลข
for(number: Int in 1..3) { ... }
for(number: Int in 10 downTo 0 step 1) { ... }
// for loop โดยค่าที่สร้างขึ้นมาเป็น Index ใน Array
for(i in items.indices) {
println(item[i])
}
// for loop ที่มีทั้งค่าใน Array และ Index โดยใช้ withIndex()
for((index, item) in items.withIndex()) {
println("$item in index $index)
}

While loops & do while

while(x > 0) {
println(x)
--x
}
do {
println(x)
--x
} while (x > 0)

If

ใช้ตรวจสอบเงื่อนไขต่างๆ

//Normal
var max: Int
if (a > b) {
max = a
} else {
max = b
}

// Shorthand
val max = if (a > b) a else b

Kotlin ยังทำให้ If มีความสามารถมากขึ้นไปอีก นอกจากจะใช้ Shorthand เพื่อกดหนดค่าให้ตัวแปรแล้ว ยังสามารถเขียนโค้ดอื่นๆลงไปได้อีก เป็นการนำ If แบบที่ Normal และ Shorthand เข้าไว้ด้วยกัน

val max = if (a > b) {
print("Choose a")
a
} else {
print("Choose b")
b
}

โดยค่าที่จะ assign ให้ max คือค่าในบรรทัดสุดท้ายในแต่ละ block เงื่อนไข เช่น ถ้า a > b ระบบจะแสดงคำว่า “Choose a” และคืนค่า a ให้ตัวแปร max

When

เป็นหนึ่งในการตรวจสอบเงื่อนไขเหมือนกันกับ if (มันคือ switch case) มีหลักการทำงานคล้ายกับ switch case ในภาษาอื่นๆ สามารถใช้ร่วมกับตัวแปร nullable ได้เป็นอย่างดี เช่น

var name: String? = "Aphirat"
val nameToPrint = when(name) {
null -> "Your name is null"
else -> "Your name is" + name!!
}
//nameToPrint = "Your name is Aphirat"when (number) {
0, 1 -> println("number is 0 or 1")
else -> print("otherwise")
}

ForEach

เป็นการวนลูปตามของที่มีอยู่ในตัวแปร Collection เช่น List, Set, Map หรือ Array ทั่วๆไป Kotlin จะสร้างตัวแปร it ขึ้นมาเพื่อแทนค่าแต่ละตัวใน array ให้อัตโนมัติ (จากโค้ดด้านล่างจะไม่สามารถมองเห็นได้ ต้องอยู่ใน Complier)

var salaries: List<Int>  = listOf(20000, 30000, 50000)
listOfSalary.forEach { //it
println(it)
}

ถ้าหากต้องการระบุชื่อตัวแปรที่ไม่ใช่ it ก็สามารถทำได้

var salaries: List<Int>  = listOf(20000, 30000, 50000)
listOfSalary.forEach { salary ->
println(salary)
}

การ forEach แบบนี้จะไม่ได้ index ค่าที่อยู่ใน aray มาด้วย ต้องใช้ function forEachIndexed

var salaries: List<Int>  = listOf(20000, 30000, 50000)
listOfSalary.forEachIndexed { index, salary ->
println("At index $index salary is $salary")
}

Collection

เป็นการรวมกลุ่มของค่าต่างๆที่เป็นประเภทเดียวกันเอาไว้ในตัวแปรตัวแปรเดียว ประกอบไปด้วย

  • List เป็นการเก็บค่าต่างๆไว้ในตัวแปรเดียว ใช้ index ในการเข้าถึงค่าแต่ละตัว เริ่มจาก 0 จนถึงจำนวนของที่อยู่ใน list-1 (N-1) สามารถใช้การเปรียบเทียบว่าเท่ากันหรือไม่ ถ้าหากมีจำนวนและของข้างในเหมือนกันก็จะได้ค่า true
var salaries: List<Int>  = listOf(20000, 30000, 50000)
listOfSalary.forEach { salary ->
println(salary)
}
  • Set เป็นการเก็บค่าต่างๆไว้ในตัวแปรเดียว แต่ค่าที่เก็บนั้นจะต้องไม่ซ้ำกัน รวมถึงค่า null ก็มีได้แต่ตัวเดียวเท่ากัน ถึงแม้มีค่าที่ซ้ำกัน ก็สามารถประกาศได้ แต่เมื่อลองสั่ง print ออกมาดู ค่าที่ซ้ำกันจะถูกทำให้เหลือตัวเดียว สามารถเปรียบความเท่ากันได้
var salaries: Set<Int> = setOf(20000, 20000, 30000, 50000)
salaries.forEachIndexed { index, salary ->
println("$salary -> index = $index")
}
//20000 -> index = 0
//30000 -> index = 1
//50000 -> index = 2

ค่าแต่ละตัวใน Set เชื่อมโยงกันเรียกว่า LinkHashSet ทำให้เราสามารถใช้ .first(), .last() เพื่อเข้าถึงข้อมูลใน Set ได้

println(salaries.first())
println(salaries.last())
//20000
//50000
  • Map เป็นการเก็บค่าต่างๆไว้ในตัวแปรเดียว โดยแต่ละค่านั้นจะมี key-value คู่กันเสมอ ปกติ List, Set จะไม่ต้องมี คือใส่ค่าเข้าไปตรงๆได้เลย แต่ Map จะต้องกำหนด key-value และ key จะต้องไม่ซ้ำกัน ทั้งนี้ value สามารถซ้ำกันได้
var salaries: Map<String, Int> = mapOf<String, Int>("1st year" to 30000, "2nd year" to 40000, "3rd year" to 50000)
salaries.forEach { key, salary ->
println("$key my salary is $salary")
}
//1st year my salary is 30000
//2nd year my salary is 40000
//3rd year my salary is 50000
println("1st year my salary is ${salaries["1st year"]}")
//1st year my salary is 30000

Mutable & Immutable Collection

การประกาศ Collection (List, Set, Map) แบบด้านบนนั้นไม่สามารถแก้ไข เพิ่ม ลด ค่าด้านในได้ เพราะเป็น Immutable ถ้าหากต้องการสร้าง Collection ให้สามารถแก้ไขได้ต้องประกาศดังนี้

var salaries: MutableList<Int> = mutableListOf(20000, 30000, 50000)
salaries.add(60000)
salaries.forEach{
println(it)
}
//20000
//30000
//50000
//60000
var salaries: MutableSet<Int> = mutableSetOf(20000, 30000, 50000)
salaries.add(60000)
salaries.forEach{
println(it)
}
//20000
//30000
//50000
//60000
var salaries: MutableMap<String, Int> = mutableMapOf<String, Int>("1st year" to 20000, "2nd year" to 30000, "3rd year" to 50000)//salaries.put("5th year", 60000) //ใช้ได้แต่ไม่แนะนำ
salaries["5th year"] = 60000 //IntelliJ แนะนำ
salaries.forEach{ key, salary ->
println("$key -> $salary")
}
//1st year -> 20000
//2nd year -> 30000
//3rd year -> 50000
//5th year -> 60000

Next Episode

LongReian: Kotlin Ep. 2: Coming soon

--

--