Kotlin Bootcamp for Programmers 5.1: Ekstensi

Codelab ini adalah bagian dari kursus Kotlin Bootcamp for Programmers. Anda akan mendapatkan manfaat maksimal dari kursus ini jika menyelesaikan codelab secara berurutan. Anda mungkin dapat membaca cepat beberapa bagian, bergantung pada pengetahuan Anda. Kursus ini ditujukan bagi programer yang menguasai bahasa berorientasi objek, dan ingin mempelajari Kotlin.

Pengantar

Dalam codelab ini, Anda akan diperkenalkan dengan sejumlah fitur berguna di Kotlin, termasuk pasangan, koleksi, dan fungsi ekstensi.

Daripada membuat satu aplikasi contoh, pelajaran dalam kursus ini dirancang untuk membangun pengetahuan Anda, tetapi bersifat semi-independen satu sama lain sehingga Anda dapat membaca sekilas bagian yang sudah Anda kuasai. Untuk mengaitkannya, banyak contoh yang menggunakan tema akuarium. Jika Anda ingin melihat kisah akuarium selengkapnya, lihat kursus Kotlin Bootcamp for Programmers di Udacity.

Yang harus sudah Anda ketahui

  • Sintaksis fungsi, class, dan metode Kotlin
  • Cara menggunakan REPL (Read-Eval-Print Loop) Kotlin di IntelliJ IDEA
  • Cara membuat class baru di IntelliJ IDEA dan menjalankan program

Yang akan Anda pelajari

  • Cara menggunakan pasangan dan tiga rangkap
  • Selengkapnya tentang koleksi
  • Menentukan dan menggunakan konstanta
  • Menulis fungsi ekstensi

Yang akan Anda lakukan

  • Mempelajari pasangan, tiga rangkap, dan peta hash di REPL
  • Mempelajari berbagai cara mengatur konstanta
  • Menulis fungsi ekstensi dan properti ekstensi

Dalam tugas ini, Anda akan mempelajari pasangan dan tiga rangkap serta cara melakukan destrukturisasi. Pasangan dan rangkap tiga adalah class data siap pakai untuk 2 atau 3 item generik. Hal ini dapat berguna, misalnya, agar fungsi menampilkan lebih dari satu nilai.

Misalkan Anda memiliki List ikan, dan fungsi isFreshWater() untuk memeriksa apakah ikan tersebut adalah ikan air tawar atau ikan air asin. List.partition() menampilkan dua daftar, satu dengan item yang kondisinya true, dan yang lainnya untuk item yang kondisinya false.

val twoLists = fish.partition { isFreshWater(it) }
println("freshwater: ${twoLists.first}")
println("saltwater: ${twoLists.second}")

Langkah 1: Buat beberapa pasangan dan tiga rangkai

  1. Buka REPL (Tools > Kotlin > Kotlin REPL).
  2. Buat pasangan, yang mengaitkan peralatan dengan penggunaannya, lalu cetak nilainya. Anda dapat membuat pasangan dengan membuat ekspresi yang menghubungkan dua nilai, seperti dua string, dengan kata kunci to, lalu menggunakan .first atau .second untuk merujuk ke setiap nilai.
val equipment = "fish net" to "catching fish"
println("${equipment.first} used for ${equipment.second}")
⇒ fish net used for catching fish
  1. Buat tiga tuple dan cetak dengan toString(), lalu konversikan ke daftar dengan toList(). Anda membuat tiga rangkap menggunakan Triple() dengan 3 nilai. Gunakan .first, .second, dan .third untuk merujuk ke setiap nilai.
val numbers = Triple(6, 9, 42)
println(numbers.toString())
println(numbers.toList())
⇒ (6, 9, 42)
[6, 9, 42]

Contoh di atas menggunakan jenis yang sama untuk semua bagian pasangan atau tiga bagian, tetapi hal itu tidak diperlukan. Bagian dapat berupa string, angka, atau daftar, misalnya—bahkan pasangan atau tiga bagian lainnya.

  1. Buat pasangan yang bagian pertama dari pasangan itu sendiri adalah pasangan.
val equipment2 = ("fish net" to "catching fish") to "equipment"
println("${equipment2.first} is ${equipment2.second}\n")
println("${equipment2.first.second}")
⇒ (fish net, catching fish) is equipment
⇒ catching fish

Langkah 2: Lakukan destrukturisasi beberapa pasangan dan tripel

Memisahkan pasangan dan tiga rangkap menjadi bagian-bagiannya disebut destrukturisasi. Tetapkan pasangan atau tiga angka ke jumlah variabel yang sesuai, dan Kotlin akan menetapkan nilai setiap bagian secara berurutan.

  1. Lakukan destrukturisasi pasangan dan cetak nilainya.
val equipment = "fish net" to "catching fish"
val (tool, use) = equipment
println("$tool is used for $use")
⇒ fish net is used for catching fish
  1. Mengurai tiga kali lipat dan mencetak nilai.
val numbers = Triple(6, 9, 42)
val (n1, n2, n3) = numbers
println("$n1 $n2 $n3")
⇒ 6 9 42

Perhatikan bahwa pasangan dan tiga kali dekonstruksi berfungsi sama seperti dengan class data, yang dibahas dalam codelab sebelumnya.

Dalam tugas ini, Anda akan mempelajari lebih lanjut koleksi, termasuk daftar, dan jenis koleksi baru, hash map.

Langkah 1: Pelajari lebih lanjut daftar

  1. Daftar dan daftar yang dapat diubah diperkenalkan dalam pelajaran sebelumnya. Struktur data ini sangat berguna, sehingga Kotlin menyediakan sejumlah fungsi bawaan untuk daftar. Tinjau daftar fungsi parsial ini untuk daftar. Anda dapat menemukan listingan lengkap di dokumentasi Kotlin untuk List dan MutableList.

Fungsi

Tujuan

add(element: E)

Menambahkan item ke daftar yang dapat diubah.

remove(element: E)

Menghapus item dari daftar yang dapat berubah.

reversed()

Menampilkan salinan daftar dengan elemen dalam urutan terbalik.

contains(element: E)

Menampilkan true jika daftar berisi item.

subList(fromIndex: Int, toIndex: Int)

Menampilkan bagian daftar, dari indeks pertama hingga, tetapi tidak termasuk, indeks kedua.

  1. Masih bekerja di REPL, buat daftar angka dan panggil sum() di dalamnya. Ini menjumlahkan semua elemen.
val list = listOf(1, 5, 3, 4)
println(list.sum())
⇒ 13
  1. Buat daftar string dan jumlahkan daftar tersebut.
val list2 = listOf("a", "bbb", "cc")
println(list2.sum())
⇒ error: none of the following functions can be called with the arguments supplied:
  1. Jika elemen bukan sesuatu yang List tahu cara menjumlahkannya secara langsung, seperti string, Anda dapat menentukan cara menjumlahkannya menggunakan .sumBy() dengan fungsi lambda, misalnya, untuk menjumlahkan berdasarkan panjang setiap string. Nama default untuk argumen lambda adalah it dan di sini it merujuk ke setiap elemen daftar saat daftar dilalui.
val list2 = listOf("a", "bbb", "cc")
println(list2.sumBy { it.length })
⇒ 6
  1. Ada banyak hal yang dapat Anda lakukan dengan daftar. Salah satu cara untuk melihat fungsi yang tersedia adalah dengan membuat daftar di IntelliJ IDEA, menambahkan titik, lalu melihat daftar pelengkapan otomatis di tooltip. Ini berfungsi untuk objek apa pun. Cobalah dengan daftar.

  1. Pilih listIterator() dari daftar, lalu buka daftar dengan pernyataan for dan cetak semua elemen yang dipisahkan dengan spasi.
val list2 = listOf("a", "bbb", "cc")
for (s in list2.listIterator()) {
    println("$s ")
}
⇒ a bbb cc

Langkah 2: Coba peta hash

Di Kotlin, Anda dapat memetakan hampir semua hal ke hal lain menggunakan hashMapOf(). Peta hash mirip dengan daftar pasangan, dengan nilai pertama bertindak sebagai kunci.

  1. Buat peta hash yang mencocokkan gejala, kunci, dan penyakit ikan, nilai.
val cures = hashMapOf("white spots" to "Ich", "red sores" to "hole disease")
  1. Kemudian, Anda dapat mengambil nilai penyakit berdasarkan kunci gejala, menggunakan get(), atau bahkan lebih singkat, tanda kurung siku [].
println(cures.get("white spots"))
⇒ Ich
println(cures["red sores"])
⇒ hole disease
  1. Coba tentukan gejala yang tidak ada di peta.
println(cures["scale loss"])
⇒ null

Jika kunci tidak ada dalam peta, mencoba menampilkan penyakit yang cocok akan menampilkan null. Bergantung pada data peta, mungkin tidak ada kecocokan untuk kemungkinan kunci. Untuk kasus seperti itu, Kotlin menyediakan fungsi getOrDefault().

  1. Coba cari kunci yang tidak memiliki kecocokan, menggunakan getOrDefault().
println(cures.getOrDefault("bloating", "sorry, I don't know"))
⇒ sorry, I don't know

Jika Anda perlu melakukan lebih dari sekadar menampilkan nilai, Kotlin menyediakan fungsi getOrElse().

  1. Ubah kode Anda agar menggunakan getOrElse(), bukan getOrDefault().
println(cures.getOrElse("bloating") {"No cure for this"})
⇒ No cure for this

Daripada menampilkan nilai default sederhana, kode apa pun yang berada di antara tanda kurung kurawal {} akan dieksekusi. Dalam contoh, else hanya menampilkan string, tetapi bisa juga berupa menemukan halaman web yang berisi solusi dan menampilkannya.

Sama seperti mutableListOf, Anda juga dapat membuat mutableMapOf. Peta yang dapat diubah memungkinkan Anda memasukkan dan menghapus item. Dapat diubah hanya berarti dapat berubah, tidak dapat diubah berarti tidak dapat berubah.

  1. Buat peta inventaris yang dapat diubah, yang memetakan string peralatan ke jumlah item. Buat dengan jaring ikan di dalamnya, lalu tambahkan 3 penggosok akuarium ke inventaris dengan put(), dan hapus jaring ikan dengan remove().
val inventory = mutableMapOf("fish net" to 1)
inventory.put("tank scrubber", 3)
println(inventory.toString())
inventory.remove("fish net")
println(inventory.toString())
⇒ {fish net=1, tank scrubber=3}{tank scrubber=3}

Dalam tugas ini, Anda akan mempelajari konstanta di Kotlin dan berbagai cara untuk mengaturnya.

Langkah 1: Pelajari const vs. val

  1. Di REPL, coba buat konstanta numerik. Di Kotlin, Anda dapat membuat konstanta level teratas dan menetapkan nilai pada waktu kompilasi menggunakan const val.
const val rocks = 3

Nilai ditetapkan, dan tidak dapat diubah, yang sangat mirip dengan mendeklarasikan val reguler. Jadi, apa perbedaan antara const val dan val? Nilai untuk const val ditentukan pada waktu kompilasi, sedangkan nilai untuk val ditentukan selama eksekusi program, yang berarti, val dapat ditetapkan oleh fungsi saat runtime.

Artinya, val dapat diberi nilai dari fungsi, tetapi const val tidak dapat.

val value1 = complexFunctionCall() // OK
const val CONSTANT1 = complexFunctionCall() // NOT ok

Selain itu, const val hanya berfungsi di tingkat teratas, dan di class singleton yang dideklarasikan dengan object, bukan dengan class reguler. Anda dapat menggunakan ini untuk membuat file atau objek singleton yang hanya berisi konstanta, dan mengimpornya sesuai kebutuhan.

object Constants {
    const val CONSTANT2 = "object constant"
}
val foo = Constants.CONSTANT2

Langkah 2: Buat objek pendamping

Kotlin tidak memiliki konsep konstanta tingkat class.

Untuk menentukan konstanta di dalam class, Anda harus membungkusnya ke dalam objek pendamping yang dideklarasikan dengan kata kunci companion. Objek pendamping pada dasarnya adalah objek singleton dalam class.

  1. Buat class dengan objek pendamping yang berisi konstanta string.
class MyClass {
    companion object {
        const val CONSTANT3 = "constant in companion"
    }
}

Perbedaan mendasar antara objek pendamping dan objek biasa adalah:

  • Objek pendamping diinisialisasi dari konstruktor statis class yang memuatnya, yaitu dibuat saat objek dibuat.
  • Objek reguler diinisialisasi secara lambat pada akses pertama ke objek tersebut; yaitu, saat pertama kali digunakan.

Ada lebih banyak hal yang perlu diketahui, tetapi untuk saat ini, Anda hanya perlu mengetahui cara membungkus konstanta dalam class di objek pendamping.

Dalam tugas ini, Anda akan mempelajari cara memperluas perilaku class. Menulis fungsi utilitas untuk memperluas perilaku class adalah hal yang sangat umum. Kotlin menyediakan sintaksis yang mudah untuk mendeklarasikan fungsi utilitas ini: fungsi ekstensi.

Fungsi ekstensi memungkinkan Anda menambahkan fungsi ke class yang sudah ada tanpa harus mengakses kode sumbernya. Misalnya, Anda dapat mendeklarasikannya dalam file Extensions.kt yang merupakan bagian dari paket Anda. Hal ini tidak benar-benar memodifikasi class, tetapi memungkinkan Anda menggunakan notasi titik saat memanggil fungsi pada objek class tersebut.

Langkah 1: Tulis fungsi ekstensi

  1. Masih bekerja di REPL, tulis fungsi ekstensi sederhana, hasSpaces() untuk memeriksa apakah string berisi spasi. Nama fungsi diawali dengan class yang mengoperasikannya. Di dalam fungsi, this merujuk ke objek yang dipanggil, dan it merujuk ke iterator dalam panggilan find().
fun String.hasSpaces(): Boolean {
    val found = this.find { it == ' ' }
    return found != null
}
println("Does it have spaces?".hasSpaces())
⇒ true
  1. Anda dapat menyederhanakan fungsi hasSpaces(). this tidak diperlukan secara eksplisit, dan fungsi dapat direduksi menjadi satu ekspresi dan ditampilkan, sehingga tanda kurung kurawal {} di sekitarnya juga tidak diperlukan.
fun String.hasSpaces() = find { it == ' ' } != null

Langkah 2: Pelajari batasan ekstensi

Fungsi ekstensi hanya memiliki akses ke API publik class yang diperluasnya. Variabel yang private tidak dapat diakses.

  1. Coba tambahkan fungsi ekstensi ke properti yang ditandai private.
class AquariumPlant(val color: String, private val size: Int)

fun AquariumPlant.isRed() = color == "red"    // OK
fun AquariumPlant.isBig() = size > 50         // gives error
⇒ error: cannot access 'size': it is private in 'AquariumPlant'
  1. Periksa kode di bawah dan cari tahu apa yang akan dicetak.
open class AquariumPlant(val color: String, private val size: Int)

class GreenLeafyPlant(size: Int) : AquariumPlant("green", size)

fun AquariumPlant.print() = println("AquariumPlant")
fun GreenLeafyPlant.print() = println("GreenLeafyPlant")

val plant = GreenLeafyPlant(size = 10)
plant.print()
println("\n")
val aquariumPlant: AquariumPlant = plant
aquariumPlant.print()  // what will it print?
⇒ GreenLeafyPlant
AquariumPlant

plant.print() cetakan foto GreenLeafyPlant. Anda mungkin berharap aquariumPlant.print() juga mencetak GreenLeafyPlant, karena telah diberi nilai plant. Namun, jenisnya diselesaikan pada waktu kompilasi, sehingga AquariumPlant dicetak.

Langkah 3: Tambahkan properti ekstensi

Selain fungsi ekstensi, Kotlin juga memungkinkan Anda menambahkan properti ekstensi. Seperti fungsi ekstensi, Anda menentukan class yang diperluas, diikuti dengan titik, lalu nama properti.

  1. Masih bekerja di REPL, tambahkan properti ekstensi isGreen ke AquariumPlant, yang merupakan true jika warnanya hijau.
val AquariumPlant.isGreen: Boolean
   get() = color == "green"

Properti isGreen dapat diakses seperti properti biasa; saat diakses, pengambil untuk isGreen dipanggil untuk mendapatkan nilai.

  1. Cetak properti isGreen untuk variabel aquariumPlant dan amati hasilnya.
aquariumPlant.isGreen
⇒ res4: kotlin.Boolean = true

Langkah 4: Ketahui penerima yang dapat bernilai null

Class yang Anda perluas disebut penerima, dan class tersebut dapat dibuat nullable. Jika Anda melakukannya, variabel this yang digunakan dalam isi dapat berupa null, jadi pastikan Anda mengujinya. Anda sebaiknya menggunakan penerima yang dapat bernilai null jika Anda memperkirakan pemanggil ingin memanggil metode ekstensi Anda pada variabel yang dapat bernilai null, atau jika Anda ingin memberikan perilaku default saat fungsi Anda diterapkan ke null.

  1. Masih bekerja di REPL, tentukan metode pull() yang menggunakan penerima yang dapat bernilai null. Hal ini ditunjukkan dengan tanda tanya ? setelah jenis, sebelum titik. Di dalam isi, Anda dapat menguji apakah this bukan null dengan menggunakan questionmark-dot-apply ?.apply.
fun AquariumPlant?.pull() {
   this?.apply {
       println("removing $this")
   }
}

val plant: AquariumPlant? = null
plant.pull()
  1. Dalam hal ini, tidak ada output saat Anda menjalankan program. Karena plant adalah null, println() dalam tidak dipanggil.

Fungsi ekstensi sangat canggih, dan sebagian besar library standar Kotlin diimplementasikan sebagai fungsi ekstensi.

Dalam pelajaran ini, Anda telah mempelajari koleksi lebih lanjut, mempelajari konstanta, dan merasakan kekuatan fungsi dan properti ekstensi.

  • Pasangan dan tiga rangkap dapat digunakan untuk menampilkan lebih dari satu nilai dari sebuah fungsi. Contoh:
    val twoLists = fish.partition { isFreshWater(it) }
  • Kotlin memiliki banyak fungsi berguna untuk List, seperti reversed(), contains(), dan subList().
  • HashMap dapat digunakan untuk memetakan kunci ke nilai. Contoh:
    val cures = hashMapOf("white spots" to "Ich", "red sores" to "hole disease")
  • Deklarasikan konstanta waktu kompilasi menggunakan kata kunci const. Anda dapat menempatkannya di tingkat teratas, menyusunnya dalam objek singleton, atau menempatkannya dalam objek pendamping.
  • Objek pendamping adalah objek singleton dalam definisi class, yang ditentukan dengan kata kunci companion.
  • Fungsi dan properti ekstensi dapat menambahkan fungsi ke class. Contoh:
    fun String.hasSpaces() = find { it == ' ' } != null
  • Penerima yang dapat bernilai null memungkinkan Anda membuat ekstensi pada class yang dapat berupa null. Operator ?. dapat dipasangkan dengan apply untuk memeriksa null sebelum menjalankan kode. Contoh:
    this?.apply { println("removing $this") }

Dokumentasi Kotlin

Jika Anda ingin mendapatkan informasi lebih lanjut tentang topik apa pun dalam kursus ini, atau jika Anda mengalami masalah, https://kotlinlang.org adalah titik awal terbaik Anda.

Tutorial Kotlin

Situs https://try.kotlinlang.org menyertakan tutorial lengkap yang disebut Kotlin Koans, interpreter berbasis web, dan serangkaian lengkap dokumentasi referensi dengan contoh.

Kursus Udacity

Untuk melihat kursus Udacity tentang topik ini, lihat Kotlin Bootcamp for Programmers.

IntelliJ IDEA

Dokumentasi untuk IntelliJ IDEA dapat ditemukan di situs JetBrains.

Bagian ini mencantumkan kemungkinan tugas pekerjaan rumah untuk siswa yang mengerjakan codelab ini sebagai bagian dari kursus yang dipimpin oleh instruktur. Instruktur menentukan hal berikut:

  • Memberikan pekerjaan rumah jika diperlukan.
  • Memberi tahu siswa cara mengirimkan tugas pekerjaan rumah.
  • Memberi nilai tugas pekerjaan rumah.

Instruktur bisa menggunakan saran ini sesuai kebutuhan, dan bebas menugaskan pekerjaan rumah lain yang dirasa cocok.

Jika Anda menyelesaikan codelab ini sendiri, gunakan tugas pekerjaan rumah ini untuk menguji pengetahuan Anda.

Jawab pertanyaan-pertanyaan berikut

Pertanyaan 1

Manakah dari opsi berikut yang menampilkan salinan daftar?

add()

remove()

reversed()

contains()

Pertanyaan 2

Manakah dari fungsi ekstensi pada class AquariumPlant(val color: String, val size: Int, private val cost: Double, val leafy: Boolean) ini yang akan memberikan error compiler?

fun AquariumPlant.isRed() = color == "red"

fun AquariumPlant.isBig() = size > 45

fun AquariumPlant.isExpensive() = cost > 10.00

fun AquariumPlant.isNotLeafy() = leafy == false

Pertanyaan 3

Manakah dari opsi berikut yang bukan tempat untuk menentukan konstanta dengan const val?

▢ di tingkat teratas file

▢ di kelas reguler

▢ dalam objek singleton

▢ di objek pendamping

Lanjutkan ke pelajaran berikutnya: 5.2 Generik

Untuk ringkasan kursus, termasuk link ke codelab lainnya, lihat "Kotlin Bootcamp for Programmers: Welcome to the course".