คุณสมบัติสิบ Kotlin เพื่อเพิ่มการพัฒนา Android

เผยแพร่แล้ว: 2022-03-11

บทนำ

เมื่อไม่นานมานี้ Tomasz ได้แนะนำการพัฒนา Kotlin บน Android เพื่อเตือนคุณ: Kotlin เป็นภาษาการเขียนโปรแกรมใหม่ที่พัฒนาโดย Jetbrains บริษัทที่อยู่เบื้องหลังหนึ่งใน Java IDE ที่ได้รับความนิยมมากที่สุด IntelliJ IDEA เช่นเดียวกับ Java Kotlin เป็นภาษาเอนกประสงค์ เนื่องจากเป็นไปตาม bytecode ของ Java Virtual Machine (JVM) จึงสามารถใช้ควบคู่ไปกับ Java ได้ และไม่มีโอเวอร์เฮดด้านประสิทธิภาพ

ในบทความนี้ ผมจะกล่าวถึงคุณสมบัติที่มีประโยชน์ 10 อันดับแรกเพื่อส่งเสริมการพัฒนา Android ของคุณ

หมายเหตุ : ในขณะที่เขียนบทความนี้ เวอร์ชันจริงคือ Android Studio 2.1.1 และ Kotlin 1.0.2

คอตลิน

เบื่อกับโค้ด Java ที่ไม่สิ้นสุดใช่ไหม ลอง Kotlin และประหยัดเวลาและสติของคุณ
ทวีต

Kotlin Setup

เนื่องจาก Kotlin ได้รับการพัฒนาโดย JetBrains จึงได้รับการสนับสนุนอย่างดีทั้งใน Android Studio และ IntelliJ

ขั้นตอนแรกคือการติดตั้งปลั๊กอิน Kotlin หลังจากทำสำเร็จแล้ว จะมีการดำเนินการใหม่สำหรับการแปลง Java เป็น Kotlin สองตัวเลือกใหม่คือ:

  1. สร้างโครงการ Android ใหม่และตั้งค่า Kotlin ในโครงการ
  2. เพิ่มการรองรับ Kotlin ให้กับโปรเจ็กต์ Android ที่มีอยู่

หากต้องการเรียนรู้วิธีสร้างโครงการ Android ใหม่ ให้ตรวจสอบคำแนะนำทีละขั้นตอนอย่างเป็นทางการ หากต้องการเพิ่มการรองรับ Kotlin ให้กับโครงการที่สร้างขึ้นใหม่หรือที่มีอยู่ ให้เปิด กล่องโต้ตอบการดำเนินการค้นหา โดยใช้ Command + Shift + A บน Mac หรือ Ctrl + Shift + A บน Windows/Linux และเรียกใช้ Configure Kotlin in Project

ในการสร้างคลาส Kotlin ใหม่ ให้เลือก:

  • File > New > Kotlin file/class หรือ
  • File > New > Kotlin activity

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

องค์ประกอบที่มีประโยชน์อีกอย่างที่ช่วยประหยัดการพิมพ์ได้มากคือส่วนขยาย Kotlin ในการใช้งาน คุณต้องใช้ปลั๊กอินอื่นในไฟล์ build.gradle โมดูลของคุณ:

 apply plugin: 'kotlin-android-extensions'

คำ เตือน : หากคุณกำลังใช้การกระทำของปลั๊กอิน Kotlin เพื่อตั้งค่าโครงการของคุณ มันจะใส่รหัสต่อไปนี้ในไฟล์ build.gradle ระดับบนสุดของคุณ:

 buildscript { ext.kotlin_version = '1.0.2' repositories { jcenter() } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } }

ซึ่งจะทำให้ส่วนขยายไม่ทำงาน ในการแก้ไขปัญหานั้น เพียงคัดลอกโค้ดนั้นไปยังแต่ละโมดูลของโปรเจ็กต์ที่คุณต้องการใช้ Kotlin

หากคุณตั้งค่าทุกอย่างถูกต้อง คุณจะสามารถเรียกใช้และทดสอบแอปพลิเคชันของคุณได้เช่นเดียวกับที่ทำในโปรเจ็กต์ Android มาตรฐาน แต่ตอนนี้ใช้ Kotlin

ประหยัดเวลาด้วย Kotlin

ดังนั้น เรามาเริ่มด้วยการอธิบายลักษณะสำคัญของภาษา Kotlin และโดยการให้คำแนะนำเกี่ยวกับวิธีการประหยัดเวลาโดยใช้แทน Java

คุณลักษณะ #1: การนำเข้าเค้าโครงแบบคงที่

หนึ่งในรหัสต้นแบบที่พบบ่อยที่สุดใน Android คือการใช้ findViewById() เพื่อรับการอ้างอิงถึงมุมมองของคุณในกิจกรรมหรือส่วนย่อย

มีวิธีแก้ไข เช่น ไลบรารี Butterknife ที่ช่วยประหยัดการพิมพ์ แต่ Kotlin ใช้อีกขั้นตอนหนึ่งโดยอนุญาตให้คุณนำเข้าข้อมูลอ้างอิงทั้งหมดไปยังมุมมองจากเลย์เอาต์ด้วยการนำเข้าครั้งเดียว

ตัวอย่างเช่น พิจารณาโครงร่าง XML ของกิจกรรมต่อไปนี้:

 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:andro xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="co.ikust.kotlintest.MainActivity"> <TextView android: android:layout_width="wrap_content" android:layout_height="wrap_content"/> </RelativeLayout>

และรหัสกิจกรรมที่มาพร้อมกับ:

 package co.ikust.kotlintest import android.support.v7.app.AppCompatActivity import android.os.Bundle import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) helloWorldTextView.text = "Hello World!" } }

หากต้องการรับข้อมูลอ้างอิงสำหรับมุมมองทั้งหมดในเลย์เอาต์ที่มี ID ที่กำหนดไว้ ให้ใช้ส่วนขยาย Android Kotlin Anko อย่าลืมพิมพ์คำสั่งนำเข้านี้:

 import kotlinx.android.synthetic.main.activity_main.*

โปรดทราบว่าคุณไม่จำเป็นต้องเขียนเครื่องหมายอัฒภาคที่ท้ายบรรทัดใน Kotlin เนื่องจากเป็นทางเลือก

TextView จากเค้าโครงถูกนำเข้าเป็นอินสแตนซ์ TextView โดยมีชื่อเท่ากับ ID ของมุมมอง อย่าสับสนกับไวยากรณ์ที่ใช้ตั้งค่าป้ายกำกับ:

 helloWorldTextView.text = "Hello World!"

เราจะพูดถึงเรื่องนี้ในไม่ช้า

คำเตือน :

  • ตรวจสอบให้แน่ใจว่าคุณนำเข้ารูปแบบที่ถูกต้อง ไม่เช่นนั้น การอ้างอิงมุมมองที่นำเข้าจะมี null
  • เมื่อใช้แฟรกเมนต์ ตรวจสอบให้แน่ใจว่าใช้การอ้างอิงมุมมองที่นำเข้า หลังจาก การเรียกใช้ฟังก์ชัน onCreateView() นำเข้าเลย์เอาต์ในฟังก์ชัน onCreateView() และใช้การอ้างอิง View เพื่อตั้งค่า UI ใน onViewCreated() การอ้างอิงจะไม่ถูกกำหนดก่อนที่ onCreateView() จะเสร็จสิ้น

คุณลักษณะ #2: การเขียนคลาส POJO ด้วย Kotlin

สิ่งที่จะช่วยประหยัดเวลาได้มากที่สุดด้วย Kotlin คือการเขียนคลาส POJO (Plain Old Java Object) ที่ใช้ในการเก็บข้อมูล ตัวอย่างเช่น ในเนื้อหาคำขอและการตอบสนองของ RESTful API ในแอปพลิเคชันที่อาศัย RESTful API จะมีหลายคลาสเช่นนั้น

ใน Kotlin คุณทำอะไรได้หลายอย่าง และรูปแบบก็กระชับ ตัวอย่างเช่น พิจารณาคลาสต่อไปนี้ใน Java:

 public class User { private String firstName; private String lastName; public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } }

เมื่อทำงานกับ Kotlin คุณไม่จำเป็นต้องเขียนคีย์เวิร์ดสาธารณะอีก โดยค่าเริ่มต้น ทุกอย่างเป็นขอบเขตสาธารณะ ตัวอย่างเช่น หากคุณต้องการประกาศคลาส คุณเพียงแค่เขียน:

 class MyClass { }

เทียบเท่ากับโค้ด Java ด้านบนใน Kotlin:

 class User { var firstName: String? = null var lastName: String? = null }

นั่นช่วยประหยัดการพิมพ์ได้มากใช่ไหม มาดูโค้ด Kotlin กัน

Kotlin ประหยัดการพิมพ์มาก

เมื่อกำหนดตัวแปรใน Kotlin มีสองตัวเลือก:

  • ตัวแปรที่ไม่แน่นอน กำหนดโดย var คีย์เวิร์ด
  • ตัวแปรที่ไม่เปลี่ยนรูป กำหนดโดยคีย์เวิร์ด val

สิ่งต่อไปที่ควรทราบคือไวยากรณ์แตกต่างจาก Java เล็กน้อย ขั้นแรก คุณประกาศชื่อตัวแปรแล้วตามด้วยประเภท นอกจากนี้ ตามค่าเริ่มต้น คุณสมบัติจะเป็นประเภทที่ไม่ใช่ค่าว่าง ซึ่งหมายความว่าคุณสมบัติเหล่านี้ไม่สามารถยอมรับ null ได้ ในการกำหนดตัวแปรให้ยอมรับ null จะต้องเพิ่มเครื่องหมายคำถามหลังประเภท เราจะพูดถึงเรื่องนี้และความปลอดภัยเป็นโมฆะใน Kotlin ในภายหลัง

สิ่งสำคัญอีกประการที่ควรทราบคือ Kotlin ไม่มีความสามารถในการประกาศฟิลด์สำหรับชั้นเรียน สามารถกำหนดคุณสมบัติเท่านั้น ดังนั้น ในกรณีนี้ firstName และ lastName เป็นคุณสมบัติที่ได้รับการกำหนดวิธีการรับ/ตัวตั้งค่าเริ่มต้น ดังที่กล่าวไว้ ใน Kotlin ทั้งคู่จะเป็นแบบสาธารณะโดยค่าเริ่มต้น

สามารถเขียนอุปกรณ์เสริมแบบกำหนดเองได้ ตัวอย่างเช่น:

 class User { var firstName: String? = null var lastName: String? = null val fullName: String? get() firstName + " " + lastName }

จากภายนอก เมื่อพูดถึงไวยากรณ์ คุณสมบัติจะทำงานเหมือนฟิลด์สาธารณะใน Java:

 val userName = user.firstName user.firstName = "John"

โปรดทราบว่าคุณสมบัติใหม่ fullName เป็นแบบอ่านอย่างเดียว (กำหนดโดยคีย์เวิร์ด val ) และมี getter แบบกำหนดเอง มันเพียงต่อท้ายชื่อและนามสกุล

ต้องกำหนดคุณสมบัติทั้งหมดใน Kotlin เมื่อประกาศหรืออยู่ในคอนสตรัคเตอร์ มีบางกรณีที่ไม่สะดวก ตัวอย่างเช่น สำหรับคุณสมบัติที่จะเริ่มต้นผ่านการแทรกการพึ่งพา ในกรณีนั้น สามารถใช้ตัวปรับแต่ง lateinit ได้ นี่คือตัวอย่าง:

 class MyClass { lateinit var firstName : String; fun inject() { firstName = "John"; } }

รายละเอียดเพิ่มเติมเกี่ยวกับคุณสมบัติสามารถพบได้ในเอกสารอย่างเป็นทางการ

คุณลักษณะ #3: การสืบทอดและตัวสร้างคลาส

Kotlin มีไวยากรณ์ที่กระชับกว่าเมื่อพูดถึงตัวสร้างเช่นกัน

ตัวสร้าง

คลาส Kotlin มีตัวสร้างหลักและตัวสร้างรองอย่างน้อยหนึ่งตัว ตัวอย่างการกำหนดคอนสตรัคเตอร์หลัก:

 class User constructor(firstName: String, lastName: String) { }

คอนสตรัคเตอร์หลักไปหลังชื่อคลาสในนิยามคลาส หากคอนสตรัคเตอร์หลักไม่มีคำอธิบายประกอบหรือตัวแก้ไขการมองเห็น คีย์เวิร์ดของคอนสตรัคเตอร์สามารถละเว้นได้:

 class Person(firstName: String) { }

โปรดทราบว่าคอนสตรัคเตอร์หลักไม่สามารถมีโค้ดใดๆ ได้ การเริ่มต้นใด ๆ ต้องทำในบล็อกโค้ด init :

 class Person(firstName: String) { init { //perform primary constructor initialization here } }

นอกจากนี้ คอนสตรัคเตอร์หลักยังสามารถใช้เพื่อกำหนดและเริ่มต้นคุณสมบัติ:

 class User(var firstName: String, var lastName: String) { // ... }

เช่นเดียวกับคุณสมบัติทั่วไป คุณสมบัติที่กำหนดจากคอนสตรัคเตอร์หลักสามารถเปลี่ยนแปลงได้ ( val ) หรือ เปลี่ยนแปลงได้ ( var )

คลาสอาจมีตัวสร้างรองเช่นกัน ไวยากรณ์สำหรับการกำหนดหนึ่งมีดังนี้:

 class User(var firstName: String, var lastName) { constructor(name: String, parent: Person) : this(name) { parent.children.add(this) } }

โปรดทราบว่าคอนสตรัคเตอร์รองทุกตัวต้องมอบหมายให้กับคอนสตรัคเตอร์หลัก ซึ่งคล้ายกับ Java ซึ่งใช้คำหลัก this :

 class User(val firstName: String, val lastName: String) { constructor(firstName: String) : this(firstName, "") { //... } }

เมื่อสร้างอินสแตนซ์ของคลาส โปรดทราบว่า Kotlin ไม่มีคีย์เวิร์ด new เช่นเดียวกับ Java หากต้องการยกตัวอย่างคลาส User ดังกล่าว ให้ใช้:

 val user = User("John", "Doe)

แนะนำมรดก

ใน Kotlin ทุกคลาสขยายจาก Any ซึ่งคล้ายกับ Object ใน Java โดยค่าเริ่มต้น คลาสจะปิด เช่นเดียวกับคลาสสุดท้ายใน Java ดังนั้น เพื่อที่จะขยายคลาส จะต้องมีการประกาศ open หรือ abstract :

 open class User(val firstName, val lastName) class Administrator(val firstName, val lastName) : User(firstName, lastName)

โปรดทราบว่าคุณต้องมอบหมายให้กับคอนสตรัคเตอร์เริ่มต้นของคลาสเสริม ซึ่งคล้ายกับการเรียกเมธอด super() ในตัวสร้างของคลาสใหม่ใน Java

สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับชั้นเรียน ตรวจสอบเอกสารอย่างเป็นทางการ

คุณลักษณะ #4: นิพจน์แลมบ์ดา

นิพจน์ Lambda ที่นำมาใช้กับ Java 8 เป็นคุณลักษณะที่ชื่นชอบอย่างหนึ่ง อย่างไรก็ตาม สิ่งต่างๆ ไม่ได้สว่างสดใสนักบน Android เนื่องจากยังคงรองรับเฉพาะ Java 7 และดูเหมือนว่า Java 8 จะไม่ได้รับการสนับสนุนในเร็วๆ นี้ ดังนั้น วิธีแก้ปัญหา เช่น Retrolambda นำนิพจน์แลมบ์ดามาสู่ Android

ด้วย Kotlin ไม่จำเป็นต้องใช้ไลบรารีหรือวิธีแก้ปัญหาเพิ่มเติม

ฟังก์ชั่นใน Kotlin

เริ่มต้นด้วยการข้ามไวยากรณ์ของฟังก์ชันใน Kotlin อย่างรวดเร็ว:

 fun add(x: Int, y: Int) : Int { return x + y }

ค่าที่ส่งคืนของฟังก์ชันสามารถละเว้นได้ และในกรณีนั้น ฟังก์ชันจะส่งกลับ Int ย้ำว่าทุกอย่างใน Kotlin เป็นวัตถุ ขยายจาก Any และไม่มีประเภทดั้งเดิม

อาร์กิวเมนต์ของฟังก์ชันสามารถมีค่าเริ่มต้นได้ ตัวอย่างเช่น

 fun add(x: Int, y: Int = 1) : Int { return x + y; }

ในกรณีนั้น ฟังก์ชัน add() สามารถเรียกใช้ได้โดยการส่งผ่านอาร์กิวเมนต์ x เท่านั้น รหัส Java ที่เทียบเท่าจะเป็น:

 int add(int x) { Return add(x, 1); } int add(int x, int y) { return x + y; }

ข้อดีอีกอย่างหนึ่งเมื่อเรียกใช้ฟังก์ชันคือสามารถใช้อาร์กิวเมนต์ที่มีชื่อได้ ตัวอย่างเช่น:

 add(y = 12, x = 5)

สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับฟังก์ชัน โปรดดูเอกสารอย่างเป็นทางการ

การใช้นิพจน์แลมบ์ดาใน Kotlin

นิพจน์แลมบ์ดาใน Kotlin สามารถดูได้ว่าเป็นฟังก์ชันที่ไม่ระบุชื่อใน Java แต่มีรูปแบบที่กระชับกว่า มาดูตัวอย่างการใช้งาน click listener ใน Java และ Kotlin

ในชวา:

 view.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(v.getContext(), "Clicked on view", Toast.LENGTH_SHORT).show(); } };

ในคอตลิน:

 view.setOnClickListener({ view -> toast("Click") })

ว้าว! รหัสเพียงหนึ่งบรรทัด! เราจะเห็นได้ว่าการแสดงออกของแลมบ์ดานั้นล้อมรอบด้วยวงเล็บปีกกา พารามิเตอร์จะถูกประกาศก่อน และเนื้อหาจะอยู่หลังเครื่องหมาย -> ด้วยตัวฟังการคลิก จะไม่มีการระบุประเภทสำหรับพารามิเตอร์การดู เนื่องจากสามารถอนุมานได้ เนื้อความเป็นเพียงการเรียกใช้ฟังก์ชัน toast() สำหรับแสดงขนมปังปิ้ง ซึ่ง Kotlin จัดเตรียมไว้ให้

นอกจากนี้ หากไม่ได้ใช้พารามิเตอร์ เราสามารถละเว้นได้:

 view.setOnClickListener({ toast("Click") })

Kotlin ได้ปรับไลบรารี Java ให้เหมาะสม และฟังก์ชันใดๆ ที่ได้รับอินเทอร์เฟซด้วยวิธีเดียวสำหรับอาร์กิวเมนต์ สามารถเรียกได้ด้วยอาร์กิวเมนต์ของฟังก์ชัน (แทนที่จะเป็น Interface)

นอกจากนี้ หากฟังก์ชันเป็นพารามิเตอร์สุดท้าย ก็สามารถย้ายออกจากวงเล็บได้:

 view.setOnClickListener() { toast("Click") }

สุดท้าย หากฟังก์ชันมีพารามิเตอร์เพียงตัวเดียวที่เป็นฟังก์ชัน วงเล็บสามารถเว้นไว้ได้:

 view.setOnClickListener { toast("Click") }

สำหรับข้อมูลเพิ่มเติม ตรวจสอบหนังสือ Kotlin สำหรับนักพัฒนา Android โดย Antonio Leiva และเอกสารอย่างเป็นทางการ

ฟังก์ชั่นส่วนขยาย

Kotlin ซึ่งคล้ายกับ C# ให้ความสามารถในการขยายคลาสที่มีอยู่ด้วยฟังก์ชันการทำงานใหม่โดยใช้ฟังก์ชันส่วนขยาย ตัวอย่างเช่น วิธีการขยายที่จะคำนวณแฮช MD5 ของ String :

 fun String.md5(): ByteArray { val digester = MessageDigest.getInstance("MD5") digester.update(this.toByteArray(Charset.defaultCharset())) return digester.digest() }

โปรดทราบว่าชื่อฟังก์ชันนำหน้าด้วยชื่อของคลาสเสริม (ในกรณีนี้คือ String ) และอินสแตนซ์ของคลาสเสริมจะพร้อมใช้งานผ่านคีย์เวิร์ด this

ฟังก์ชันส่วนขยายเทียบเท่ากับฟังก์ชันยูทิลิตี้ Java ฟังก์ชันตัวอย่างใน Java จะมีลักษณะดังนี้:

 public static int toNumber(String instance) { return Integer.valueOf(instance); }

ต้องวางฟังก์ชันตัวอย่างในคลาสยูทิลิตี้ นั่นหมายความว่าฟังก์ชันส่วนขยายไม่ได้แก้ไขคลาสขยายดั้งเดิม แต่เป็นวิธีที่สะดวกในการเขียนวิธีการยูทิลิตี้

คุณลักษณะ #5: Null-ความปลอดภัย

สิ่งหนึ่งที่คุณเร่งรีบที่สุดใน Java น่าจะเป็น NullPointerException Null-safety เป็นคุณสมบัติที่รวมเข้ากับภาษา Kotlin และโดยปริยาย ปกติแล้วคุณไม่ต้องกังวล เอกสารอย่างเป็นทางการระบุว่าสาเหตุที่เป็นไปได้เพียงอย่างเดียวของ NullPointerExceptions คือ:

  • การเรียกที่ชัดเจนเพื่อโยน NullPointerException
  • การใช้ !! โอเปอเรเตอร์ (ซึ่งฉันจะอธิบายในภายหลัง)
  • รหัส Java ภายนอก
  • หากมีการเข้าถึงคุณสมบัติ lateinit ในตัวสร้างก่อนที่จะมีการกำหนดค่าเริ่มต้น UninitializedPropertyAccessException จะถูกส่งออกไป

โดยค่าเริ่มต้น ตัวแปรและคุณสมบัติทั้งหมดใน Kotlin จะถือว่า non-null (ไม่สามารถเก็บ null ได้) หากไม่ได้ประกาศอย่างชัดแจ้งว่าเป็นค่าว่าง ดังที่ได้กล่าวไปแล้ว ในการกำหนดตัวแปรให้ยอมรับ null จะต้องเพิ่มเครื่องหมายคำถามหลังประเภท ตัวอย่างเช่น:

 val number: Int? = null

อย่างไรก็ตาม โปรดทราบว่าโค้ดต่อไปนี้จะไม่คอมไพล์:

 val number: Int? = null number.toString()

เนื่องจากคอมไพเลอร์ทำการตรวจสอบ null ในการคอมไพล์ ต้องเพิ่มการตรวจสอบ null :

 val number: Int? = null if(number != null) { number.toString(); }

รหัสนี้จะคอมไพล์สำเร็จ สิ่งที่ Kotlin ทำในพื้นหลังในกรณีนี้คือ number นั้นกลายเป็น nun-null ( Int แทนที่จะเป็น Int? ) ภายใน if block

การตรวจสอบ null สามารถทำให้ง่ายขึ้นได้โดยใช้ ตัวดำเนินการการโทรที่ปลอดภัย ( ?. ):

 val number: Int? = null number?.toString()

บรรทัดที่สองจะดำเนินการก็ต่อเมื่อตัวเลขไม่เป็น null คุณสามารถใช้ตัว ดำเนินการ Elvis ที่มีชื่อเสียง ( ?: ):

 val number Int? = null val stringNumber = number?.toString() ?: "Number is null"

หากนิพจน์ทางด้านซ้ายของ ?: ไม่ใช่ null จะถูกประเมินและส่งคืน มิฉะนั้น ผลลัพธ์ของนิพจน์ทางด้านขวาจะถูกส่งกลับ อีกสิ่งที่เรียบร้อยคือคุณสามารถใช้ throw หรือ return ทางด้านขวาของตัวดำเนินการ Elvis เนื่องจากเป็นนิพจน์ใน Kotlin ตัวอย่างเช่น:

 fun sendMailToUser(user: User) { val email = user?.email ?: throw new IllegalArgumentException("User email is null") //... }

ที่ !! โอเปอเรเตอร์

หากคุณต้องการให้ NullPointerException ถูกส่งออกไปแบบเดียวกับใน Java คุณสามารถทำได้ด้วย !! โอเปอเรเตอร์ รหัสต่อไปนี้จะส่ง NullPointerException :

 val number: Int? = null number!!.toString()

การคัดเลือกนักแสดง

หล่อเสร็จโดยใช้ as คีย์เวิร์ด:

 val x: String = y as String

นี่ถือเป็นการแคสต์ "ไม่ปลอดภัย" เนื่องจากจะโยน ClassCastException หากการแคสต์เป็นไปไม่ได้ เช่นเดียวกับ Java ทำ มีโอเปอเรเตอร์ cast "ปลอดภัย" ที่คืน null แทนที่จะส่งข้อยกเว้น:

 val x: String = y as? String

สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับการแคสต์ โปรดดูที่ส่วน Type Casts and Casts ของเอกสารอย่างเป็นทางการ และสำหรับรายละเอียดเพิ่มเติมเกี่ยวกับ null safety ให้ตรวจสอบส่วน Null-Safety

คุณสมบัติ lateinit

มีกรณีที่การใช้คุณสมบัติ lateinit อาจทำให้เกิดข้อยกเว้นที่คล้ายกับ NullPointerException พิจารณาชั้นเรียนต่อไปนี้:

 class InitTest { lateinit var s: String; init { val len = this.s.length } }

รหัสนี้จะคอมไพล์โดยไม่มีการเตือน อย่างไรก็ตาม ทันทีที่มีการสร้างอินสแตนซ์ของ TestClass จะมีการยกเลิก UninitializedPropertyAccessException เนื่องจากคุณสมบัติ s เข้าถึงก่อนที่จะเริ่มต้น

คุณลักษณะ #6: ฟังก์ชัน with()

ฟังก์ชัน with() มีประโยชน์และมาพร้อมกับไลบรารีมาตรฐานของ Kotlin สามารถใช้เพื่อบันทึกการพิมพ์บางอย่าง หากคุณต้องการเข้าถึงคุณสมบัติหลายอย่างของวัตถุ ตัวอย่างเช่น:

 with(helloWorldTextView) { text = "Hello World!" visibility = View.VISIBLE }

รับวัตถุและฟังก์ชันส่วนขยายเป็นพารามิเตอร์ โค้ดบล็อก (ในวงเล็บปีกกา) เป็นนิพจน์แลมบ์ดาสำหรับฟังก์ชันส่วนขยายของอ็อบเจ็กต์ที่ระบุเป็นพารามิเตอร์แรก

คุณลักษณะ #7: ตัวดำเนินการโอเวอร์โหลด

ด้วย Kotlin คุณสามารถจัดเตรียมการใช้งานแบบกำหนดเองสำหรับชุดตัวดำเนินการที่กำหนดไว้ล่วงหน้า ในการใช้งานตัวดำเนินการ ต้องระบุฟังก์ชันสมาชิกหรือฟังก์ชันส่วนขยายที่มีชื่อที่กำหนด

ตัวอย่างเช่น ในการใช้งานตัวดำเนินการการคูณ ต้องระบุฟังก์ชันสมาชิกหรือฟังก์ชันส่วนขยายพร้อมชื่อ times(argument) :

 operator fun String.times(b: Int): String { val buffer = StringBuffer() for (i in 1..b) { buffer.append(this) } return buffer.toString() }

ตัวอย่างข้างต้นแสดงการใช้งานตัวดำเนินการไบนารี * บน String ตัวอย่างเช่น นิพจน์ต่อไปนี้จะกำหนดค่า “TestTestTestTest” ให้กับตัวแปร newString :

 val newString = "Test" * 4

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

ความแตกต่างที่สำคัญอีกประการหนึ่งเมื่อเทียบกับ Java คือตัวดำเนินการ == และ != โอเปอเรเตอร์ == แปลเป็น:

 a?.equals(b) ?: b === null

ในขณะที่โอเปอเรเตอร์ != แปลเป็น:

 !(a?.equals(b) ?:

ความหมายก็คือการใช้ == ไม่ได้ทำการตรวจสอบตัวตนเหมือนใน Java (เปรียบเทียบว่าอินสแตนซ์ของวัตถุเหมือนกันหรือไม่) แต่ทำงานในลักษณะเดียวกับเมธอด equals() ควบคู่ไปกับการตรวจสอบ null

ในการดำเนินการตรวจสอบข้อมูลประจำตัว ต้องใช้ตัวดำเนินการ === และ !== ใน Kotlin

คุณลักษณะ #8: คุณสมบัติที่ได้รับมอบอำนาจ

คุณสมบัติบางอย่างมีพฤติกรรมร่วมกันบางอย่าง ตัวอย่างเช่น:

  • คุณสมบัติเริ่มต้นแบบ Lazy ที่เริ่มต้นเมื่อเข้าถึงครั้งแรก
  • คุณสมบัติที่ใช้ Observable ในรูปแบบ Observer
  • คุณสมบัติที่เก็บไว้ในแผนที่แทนเป็นฟิลด์แยก

เพื่อให้กรณีเช่นนี้ง่ายต่อการใช้งาน Kotlin รองรับ Delegated Properties :

 class SomeClass { var p: String by Delegate() }

ซึ่งหมายความว่าฟังก์ชัน getter และ setter สำหรับคุณสมบัติ p ได้รับการจัดการโดยอินสแตนซ์ของคลาสอื่น Delegate

ตัวอย่างของผู้รับมอบสิทธิ์สำหรับคุณสมบัติ String :

 class Delegate { operator fun getValue(thisRef: Any?, property: KProperty<*>): String { return "$thisRef, thank you for delegating '${property.name}' to me!" } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { println("$value has been assigned to '${property.name} in $thisRef.'") } }

ตัวอย่างด้านบนจะพิมพ์ข้อความเมื่อมีการกำหนดหรืออ่านคุณสมบัติ

สามารถสร้างผู้รับมอบสิทธิ์สำหรับคุณสมบัติที่ไม่แน่นอน ( var ) และแบบอ่านอย่างเดียว ( val )

สำหรับคุณสมบัติอ่านอย่างเดียว ต้องใช้วิธี getValue ใช้พารามิเตอร์สองตัว (นำมาจากเอกสารอย่างเป็นทางการ):

  • ผู้รับ - ต้องเหมือนกันหรือเป็น supertype ของเจ้าของทรัพย์สิน (สำหรับคุณสมบัติส่วนขยายเป็นประเภทที่ขยาย)
  • metadata - ต้องเป็นประเภท KProperty<*> หรือ supertype

ฟังก์ชันนี้ต้องส่งคืนประเภทเดียวกันกับคุณสมบัติหรือประเภทย่อย

สำหรับคุณสมบัติที่เปลี่ยนแปลงได้ ผู้รับมอบสิทธิ์ต้องจัดเตรียมฟังก์ชันเพิ่มเติมที่ชื่อ setValue ซึ่งรับพารามิเตอร์ต่อไปนี้:

  • ผู้รับ - เช่นเดียวกับ getValue()
  • ข้อมูลเมตา - เหมือนกับ getValue()
  • ค่าใหม่ - ต้องเป็นประเภทเดียวกับคุณสมบัติหรือ supertype

มีผู้แทนมาตรฐานสองสามรายที่มาพร้อมกับ Kotlin ที่ครอบคลุมสถานการณ์ทั่วไปส่วนใหญ่:

  • ขี้เกียจ
  • สังเกตได้
  • Vetoable

ขี้เกียจ

Lazy คือตัวแทนมาตรฐานที่ใช้นิพจน์แลมบ์ดาเป็นพารามิเตอร์ นิพจน์แลมบ์ดาที่ส่งผ่านจะถูกดำเนินการในครั้งแรกที่เรียกใช้เมธอด getValue()

ตามค่าเริ่มต้น การประเมินคุณสมบัติสันหลังยาวจะถูกซิงโครไนซ์ ถ้าคุณไม่กังวลเกี่ยวกับมัลติเธรด คุณสามารถใช้ lazy(LazyThreadSafetyMode.NONE) { … } เพื่อรับประสิทธิภาพพิเศษ

สังเกตได้

Delegates.observable() ใช้สำหรับคุณสมบัติที่ควรทำหน้าที่เป็น Observables ในรูปแบบ Observer ยอมรับพารามิเตอร์สองตัว ค่าเริ่มต้น และฟังก์ชันที่มีอาร์กิวเมนต์สามตัว (คุณสมบัติ ค่าเดิม และค่าใหม่)

นิพจน์แลมบ์ดาที่กำหนดจะถูกดำเนินการทุกครั้งที่มีการเรียกใช้ setValue() :

 class User { var email: String by Delegates.observable("") { prop, old, new -> //handle the change from old to new value } }

Vetoable

ผู้รับมอบสิทธิ์มาตรฐานนี้เป็น Observable ชนิดพิเศษที่ให้คุณตัดสินใจว่าจะจัดเก็บค่าใหม่ที่กำหนดให้กับคุณสมบัติหรือไม่ สามารถใช้ตรวจสอบเงื่อนไขบางอย่างก่อนกำหนดค่าได้ เช่นเดียวกับ Delegates.observable() จะยอมรับพารามิเตอร์สองตัว: ค่าเริ่มต้นและฟังก์ชัน

ข้อแตกต่างคือฟังก์ชันส่งกลับค่าบูลีน หากคืนค่า true ค่าใหม่ที่กำหนดให้กับคุณสมบัติจะถูกเก็บไว้หรือละทิ้ง

 var positiveNumber = Delegates.vetoable(0) { d, old, new -> new >= 0 }

ตัวอย่างที่ให้มาจะเก็บเฉพาะจำนวนบวกที่กำหนดให้กับพร็อพเพอร์ตี้

สำหรับรายละเอียดเพิ่มเติม โปรดตรวจสอบเอกสารอย่างเป็นทางการ

คุณลักษณะ #9: การทำแผนที่วัตถุไปยังแผนที่

กรณีใช้งานทั่วไปคือการจัดเก็บค่าของคุณสมบัติภายในแผนที่ สิ่งนี้มักเกิดขึ้นในแอปพลิเคชันที่ทำงานกับ RESTful API และแยกวิเคราะห์วัตถุ JSON ในกรณีนี้ อินสแตนซ์แผนที่สามารถใช้เป็นผู้รับมอบสิทธิ์สำหรับคุณสมบัติที่ได้รับมอบสิทธิ์ ตัวอย่างจากเอกสารอย่างเป็นทางการ:

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

ในตัวอย่างนี้ User มีตัวสร้างหลักที่ใช้แผนที่ คุณสมบัติทั้งสองจะใช้ค่าจากแผนที่ที่แมปภายใต้คีย์ที่เท่ากับชื่อคุณสมบัติ:

 val user = User(mapOf( "name" to "John Doe", "age" to 25 ))

คุณสมบัติชื่อของอินสแตนซ์ผู้ใช้ใหม่จะได้รับการกำหนดมูลค่าของ “John Doe” และคุณสมบัติอายุเป็นค่า 25

สิ่งนี้ใช้ได้กับคุณสมบัติของ var ร่วมกับ MutableMap เช่นกัน:

 class MutableUser(val map: MutableMap<String, Any?>) { var name: String by map var age: Int by map }

ฟีเจอร์ #10: คอลเลกชั่นและการใช้งานจริง

ด้วยการรองรับ lambdas ใน Kotlin คอลเลกชั่นสามารถยกระดับไปสู่ระดับใหม่ได้

ประการแรก Kotlin แยกความแตกต่างระหว่างคอลเลกชันที่ไม่แน่นอนและไม่เปลี่ยนรูป ตัวอย่างเช่น มีอินเทอร์เฟซ Iterable สองเวอร์ชัน:

  • Iterable
  • MutableIterable

เช่นเดียวกับอินเทอร์เฟซ Collection , List , Set และ Map

ตัวอย่างเช่น การดำเนินการ any นี้คืนค่า true หากองค์ประกอบอย่างน้อยหนึ่งรายการตรงกับเพรดิเคตที่กำหนด:

 val list = listOf(1, 2, 3, 4, 5, 6) assertTrue(list.any { it % 2 == 0 })

สำหรับรายการการทำงานที่กว้างขวางที่สามารถทำได้ในคอลเล็กชัน ให้ดูที่โพสต์ในบล็อกนี้

บทสรุป

เราเพิ่งเกาพื้นผิวของสิ่งที่ Kotlin นำเสนอ สำหรับผู้ที่สนใจอ่านเพิ่มเติมและเรียนรู้เพิ่มเติมตรวจสอบ:

  • บล็อกโพสต์และหนังสือ Kotlin ของ Antonio Leiva
  • เอกสารและแบบฝึกหัดอย่างเป็นทางการจาก JetBrains

โดยสรุป Kotlin มอบความสามารถในการประหยัดเวลาเมื่อเขียนแอปพลิเคชัน Android ดั้งเดิมโดยใช้ไวยากรณ์ที่เข้าใจง่ายและรัดกุม มันยังเป็นภาษาการเขียนโปรแกรมรุ่นใหม่ แต่ในความคิดของฉัน ตอนนี้มันเสถียรพอที่จะใช้สำหรับสร้างแอพที่ใช้งานจริงได้

ประโยชน์ของการใช้ Kotlin:

  • การสนับสนุนโดย Android Studio นั้นราบรื่นและยอดเยี่ยม
  • ง่ายต่อการแปลงโปรเจ็กต์ Java ที่มีอยู่เป็น Kotlin
  • โค้ด Java และ Kotlin อาจอยู่ร่วมกันในโปรเจ็กต์เดียวกัน
  • ไม่มีค่าใช้จ่ายความเร็วในแอปพลิเคชัน

ข้อเสีย:

  • Kotlin จะเพิ่มไลบรารีลงในไฟล์ .apk ที่สร้างขึ้น ดังนั้นขนาด .apk สุดท้ายจะใหญ่ขึ้นประมาณ 300KB
  • หากใช้งานในทางที่ผิด การโอเวอร์โหลดของผู้ปฏิบัติงานอาจทำให้โค้ดอ่านไม่ได้
  • IDE และการเติมข้อความอัตโนมัติจะทำงานช้าลงเล็กน้อยเมื่อทำงานกับ Kotlin มากกว่าที่ทำกับโปรเจ็กต์ Java Android ล้วนๆ
  • เวลาในการรวบรวมอาจนานกว่านี้เล็กน้อย