Codelab ini adalah bagian dari kursus Bootcamp Kotlin untuk Programer. Anda akan mendapatkan manfaat maksimal dari kursus ini jika Anda mengerjakan 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 membuat program Kotlin dan mempelajari class serta objek di Kotlin. Sebagian besar konten ini akan tidak asing lagi jika Anda mengetahui bahasa berorientasi objek lainnya, tetapi Kotlin memiliki beberapa perbedaan penting untuk mengurangi jumlah kode yang perlu Anda tulis. Anda juga mempelajari class abstrak dan delegasi antarmuka.
Daripada membuat aplikasi sampel tunggal, pelajaran dalam kursus ini didesain untuk membangun pengetahuan Anda, tetapi bersifat semi-mandiri, sehingga Anda dapat membaca cepat bagian yang sudah Anda pahami. Untuk menyatukannya, banyak contoh menggunakan tema akuarium. Dan jika Anda ingin melihat cerita lengkap akuarium, lihat kursus Udacity Kotlin Bootcamp for Programmers.
Yang harus sudah Anda ketahui
- Dasar-dasar Kotlin, termasuk jenis, operator, dan loop
- Sintaksis fungsi Kotlin
- Dasar-dasar pemrograman berorientasi objek
- Dasar-dasar IDE seperti IntelliJ IDEA atau Android Studio
Yang akan Anda pelajari
- Cara membuat class dan mengakses properti di Kotlin
- Cara membuat dan menggunakan konstruktor class di Kotlin
- Cara membuat subclass dan cara kerja warisan
- Tentang class abstrak, antarmuka, dan delegasi antarmuka
- Cara membuat dan menggunakan class data
- Cara menggunakan singleton, enum, dan class tertutup
Yang akan Anda lakukan
- Membuat class dengan properti
- Membuat konstruktor untuk class
- Membuat subclass
- Memeriksa contoh class dan antarmuka abstrak
- Membuat class data sederhana
- Mempelajari singleton, enum, dan class tertutup
Istilah pemrograman berikut seharusnya sudah tidak asing bagi Anda:
- Class adalah blueprint untuk objek. Misalnya, class
Aquarium
adalah blueprint untuk membuat objek akuarium. - Objek adalah instance class; objek akuarium adalah satu
Aquarium
yang sebenarnya. - Properti adalah karakteristik class, seperti panjang, lebar, dan tinggi
Aquarium
. - Metode, juga disebut fungsi anggota, adalah fungsi class. Metode adalah hal yang dapat "lakukan" dengan objek. Misalnya, Anda dapat
fillWithWater()
objekAquarium
. - Antarmuka adalah spesifikasi yang dapat diterapkan oleh class. Misalnya, pembersihan bersifat umum untuk objek selain akuarium, dan pembersihan umumnya dilakukan dengan cara yang sama untuk objek yang berbeda. Jadi, Anda mungkin memiliki antarmuka bernama
Clean
yang menentukan metodeclean()
. ClassAquarium
dapat mengimplementasikan antarmukaClean
untuk membersihkan akuarium dengan spons lembut. - Paket adalah cara untuk mengelompokkan kode terkait agar tetap teratur, atau untuk membuat library kode. Setelah paket dibuat, Anda dapat mengimpor konten paket ke dalam file lain dan menggunakan kembali kode dan class di dalamnya.
Dalam tugas ini, Anda akan membuat paket dan class baru dengan beberapa properti dan metode.
Langkah 1: Buat paket
Paket dapat membantu Anda menjaga kode tetap teratur.
- Di panel Project, di bagian project Hello Kotlin, klik kanan pada folder src.
- Pilih New > Package dan beri nama
example.myapp
.
Langkah 2: Buat class dengan properti
Class didefinisikan dengan kata kunci class
, dan nama class berdasarkan konvensi dimulai dengan huruf kapital.
- Klik kanan paket example.myapp.
- Pilih New > Kotlin File / Class.
- Di bagian Kind, pilih Class, dan beri nama class
Aquarium
. IntelliJ IDEA menyertakan nama paket dalam file dan membuat classAquarium
kosong untuk Anda. - Di dalam class
Aquarium
, tentukan dan inisialisasi propertivar
untuk lebar, tinggi, dan panjang (dalam sentimeter). Lakukan inisialisasi properti dengan nilai default.
package example.myapp
class Aquarium {
var width: Int = 20
var height: Int = 40
var length: Int = 100
}
Di balik layar, Kotlin secara otomatis membuat pengambil dan penyetel untuk properti yang Anda tentukan di class Aquarium
, sehingga Anda dapat mengakses properti secara langsung, misalnya, myAquarium.length
.
Langkah 3: Buat fungsi main()
Buat file baru bernama main.kt
untuk menyimpan fungsi main()
.
- Di panel Project di sebelah kiri, klik kanan paket example.myapp.
- Pilih New > Kotlin File/Class.
- Pada dropdown Kind, biarkan pilihan sebagai File, dan beri nama file
main.kt
. IntelliJ IDEA menyertakan nama paket, tetapi tidak termasuk definisi class untuk file. - Tentukan fungsi
buildAquarium()
dan di dalamnya, buat instanceAquarium
. Untuk membuat instance, rujuk class seolah-olah itu adalah sebuah fungsi,Aquarium()
. Tindakan ini akan memanggil konstruktor class dan membuat instance classAquarium
, mirip dengan penggunaannew
dalam bahasa lain. - Tentukan fungsi
main()
dan panggilbuildAquarium()
.
package example.myapp
fun buildAquarium() {
val myAquarium = Aquarium()
}
fun main() {
buildAquarium()
}
Langkah 4: Tambahkan metode
- Di class
Aquarium
, tambahkan metode untuk mencetak properti dimensi akuarium.
fun printSize() {
println("Width: $width cm " +
"Length: $length cm " +
"Height: $height cm ")
}
- Di
main.kt
, dibuildAquarium()
, panggil metodeprintSize()
dimyAquarium
.
fun buildAquarium() {
val myAquarium = Aquarium()
myAquarium.printSize()
}
- Jalankan program dengan mengklik segitiga hijau di samping fungsi
main()
. Amati hasilnya.
⇒ Width: 20 cm Length: 100 cm Height: 40 cm
- Di
buildAquarium()
, tambahkan kode untuk menetapkan tinggi ke 60 dan mencetak properti dimensi yang diubah.
fun buildAquarium() {
val myAquarium = Aquarium()
myAquarium.printSize()
myAquarium.height = 60
myAquarium.printSize()
}
- Jalankan program Anda dan amati outputnya.
⇒ Width: 20 cm Length: 100 cm Height: 40 cm Width: 20 cm Length: 100 cm Height: 60 cm
Dalam tugas ini, Anda akan membuat konstruktor untuk class dan terus menggunakan properti.
Langkah 1: Buat konstruktor
Pada langkah ini, Anda menambahkan konstruktor ke class Aquarium
yang Anda buat di tugas pertama. Pada contoh sebelumnya, setiap instance Aquarium
dibuat dengan dimensi yang sama. Anda dapat mengubah dimensi setelah dibuat dengan menetapkan properti tersebut, tetapi akan lebih mudah untuk membuat ukuran yang tepat untuk memulai.
Dalam beberapa bahasa pemrograman, konstruktor didefinisikan dengan membuat metode dalam class yang memiliki nama yang sama dengan class. Di Kotlin, Anda menentukan konstruktor secara langsung dalam deklarasi class itu sendiri, menentukan parameter dalam tanda kurung seolah-olah class tersebut adalah sebuah metode. Seperti fungsi di Kotlin, parameter tersebut dapat menyertakan nilai default.
- Di class
Aquarium
yang Anda buat sebelumnya, ubah definisi class untuk menyertakan tiga parameter konstruktor dengan nilai default untuklength
,width
danheight
, dan tetapkan ke properti yang sesuai.
class Aquarium(length: Int = 100, width: Int = 20, height: Int = 40) {
// Dimensions in cm
var length: Int = length
var width: Int = width
var height: Int = height
...
}
- Cara Kotlin yang lebih ringkas adalah menentukan properti secara langsung dengan konstruktor menggunakan
var
atauval
, dan Kotlin juga membuat pengambil dan penyetel secara otomatis. Kemudian Anda dapat menghapus definisi properti dalam isi class.
class Aquarium(var length: Int = 100, var width: Int = 20, var height: Int = 40) {
...
}
- Saat membuat objek
Aquarium
dengan konstruktor tersebut, Anda dapat menentukan tanpa argumen dan mendapatkan nilai default, atau menentukan beberapa dari nilai tersebut, atau menentukan semuanya dan membuatAquarium
yang sepenuhnya kustom. Pada fungsibuildAquarium()
, coba berbagai cara membuat objekAquarium
menggunakan parameter bernama.
fun buildAquarium() {
val aquarium1 = Aquarium()
aquarium1.printSize()
// default height and length
val aquarium2 = Aquarium(width = 25)
aquarium2.printSize()
// default width
val aquarium3 = Aquarium(height = 35, length = 110)
aquarium3.printSize()
// everything custom
val aquarium4 = Aquarium(width = 25, height = 35, length = 110)
aquarium4.printSize()
}
- Jalankan program dan amati outputnya.
⇒ Width: 20 cm Length: 100 cm Height: 40 cm Width: 25 cm Length: 100 cm Height: 40 cm Width: 20 cm Length: 110 cm Height: 35 cm Width: 25 cm Length: 110 cm Height: 35 cm
Perhatikan bahwa Anda tidak harus membebani konstruktor dan menulis versi yang berbeda untuk setiap kasus ini (ditambah beberapa lagi untuk kombinasi lainnya). Kotlin membuat apa yang diperlukan dari nilai default dan parameter bernama.
Langkah 2: Tambahkan blok init
Contoh konstruktor di atas cukup mendeklarasikan properti dan menetapkan nilai ekspresi ke properti tersebut. Jika konstruktor Anda memerlukan lebih banyak kode inisialisasi, konstruktor dapat ditempatkan dalam satu atau beberapa blok init
. Pada langkah ini, Anda menambahkan beberapa blok init
ke class Aquarium
.
- Di class
Aquarium
, tambahkan blokinit
untuk mencetak bahwa objek sedang diinisialisasi, dan blok kedua untuk mencetak volume dalam liter.
class Aquarium (var length: Int = 100, var width: Int = 20, var height: Int = 40) {
init {
println("aquarium initializing")
}
init {
// 1 liter = 1000 cm^3
println("Volume: ${width * length * height / 1000} l")
}
}
- Jalankan program dan amati outputnya.
aquarium initializing
Volume: 80 l
Width: 20 cm Length: 100 cm Height: 40 cm
aquarium initializing
Volume: 100 l
Width: 25 cm Length: 100 cm Height: 40 cm
aquarium initializing
Volume: 77 l
Width: 20 cm Length: 110 cm Height: 35 cm
aquarium initializing
Volume: 96 l
Width: 25 cm Length: 110 cm Height: 35 cm
Perhatikan bahwa blok init
dieksekusi dalam urutan kemunculannya dalam definisi class, dan semuanya dijalankan saat konstruktor dipanggil.
Langkah 3: Pelajari konstruktor sekunder
Pada langkah ini, Anda akan mempelajari konstruktor sekunder dan menambahkannya ke class Anda. Selain konstruktor utama, yang dapat memiliki satu atau beberapa blok init
, class Kotlin juga dapat memiliki satu atau beberapa konstruktor sekunder untuk memungkinkan kelebihan beban konstruktor, yaitu konstruktor dengan argumen yang berbeda.
- Di class
Aquarium
, tambahkan konstruktor sekunder yang menggunakan sejumlah ikan sebagai argumennya, menggunakan kata kunciconstructor
. Membuat properti tangkival
untuk volume akuarium yang dihitung dalam liter berdasarkan jumlah ikan. Asumsikan 2 liter (2.000 cm^3) air per ikan, ditambah sedikit ruang ekstra agar air tidak tumpah.
constructor(numberOfFish: Int) : this() {
// 2,000 cm^3 per fish + extra room so water doesn't spill
val tank = numberOfFish * 2000 * 1.1
}
- Di dalam konstruktor sekunder, pertahankan panjang dan lebar (yang disetel dalam konstruktor utama) tetap sama, dan hitung tinggi yang diperlukan untuk membuat volume sesuai dengan ukuran tangki.
// calculate the height needed
height = (tank / (length * width)).toInt()
- Dalam fungsi
buildAquarium()
, tambahkan panggilan untuk membuatAquarium
menggunakan konstruktor sekunder baru. Cetak ukuran dan volume.
fun buildAquarium() {
val aquarium6 = Aquarium(numberOfFish = 29)
aquarium6.printSize()
println("Volume: ${aquarium6.width * aquarium6.length * aquarium6.height / 1000} l")
}
- Jalankan program Anda dan amati outputnya.
⇒ aquarium initializing Volume: 80 l Width: 20 cm Length: 100 cm Height: 31 cm Volume: 62 l
Perhatikan bahwa volume dicetak dua kali, sekali oleh blok init
di konstruktor utama sebelum konstruktor sekunder dijalankan, dan sekali oleh kode dalam buildAquarium()
.
Anda juga dapat menyertakan kata kunci constructor
di konstruktor utama, tetapi dalam banyak kasus, kata kunci tersebut tidak diperlukan.
Langkah 4: Tambahkan pengambil properti baru
Pada langkah ini, Anda menambahkan pengambil properti eksplisit. Kotlin secara otomatis menentukan pengambil dan penyetel saat Anda menentukan properti, tetapi terkadang nilai untuk properti perlu disesuaikan atau dihitung. Misalnya, di atas, Anda mencetak volume Aquarium
. Anda dapat membuat volume tersedia sebagai properti dengan menentukan variabel dan pengambil untuk properti tersebut. Karena volume
perlu dihitung, pengambil harus menampilkan nilai yang dihitung, yang dapat Anda lakukan dengan fungsi satu baris.
- Di class
Aquarium
, tentukan propertiInt
yang disebutvolume
, dan tentukan metodeget()
yang menghitung volume di baris berikutnya.
val volume: Int
get() = width * height * length / 1000 // 1000 cm^3 = 1 l
- Hapus blok
init
yang mencetak volume. - Hapus kode dalam
buildAquarium()
yang mencetak volume. - Pada metode
printSize()
, tambahkan baris untuk mencetak volume.
fun printSize() {
println("Width: $width cm " +
"Length: $length cm " +
"Height: $height cm "
)
// 1 l = 1000 cm^3
println("Volume: $volume l")
}
- Jalankan program Anda dan amati outputnya.
⇒ aquarium initializing Width: 20 cm Length: 100 cm Height: 31 cm Volume: 62 l
Dimensi dan volume sama seperti sebelumnya, tetapi volume hanya dicetak sekali setelah objek diinisialisasi sepenuhnya oleh konstruktor utama dan konstruktor sekunder.
Langkah 5: Tambahkan penyetel properti
Pada langkah ini, Anda membuat penyetel properti baru untuk volume.
- Di class
Aquarium
, ubahvolume
menjadivar
sehingga dapat disetel lebih dari sekali. - Tambahkan penyetel untuk properti
volume
dengan menambahkan metodeset()
di bawah pengambil, yang menghitung ulang tinggi berdasarkan jumlah air yang diberikan. Berdasarkan konvensi, nama parameter penyetel adalahvalue
, tetapi Anda dapat mengubahnya jika diinginkan.
var volume: Int
get() = width * height * length / 1000
set(value) {
height = (value * 1000) / (width * length)
}
- Di
buildAquarium()
, tambahkan kode untuk menyetel volume Akuarium ke 70 liter. Cetak ukuran baru.
fun buildAquarium() {
val aquarium6 = Aquarium(numberOfFish = 29)
aquarium6.printSize()
aquarium6.volume = 70
aquarium6.printSize()
}
- Jalankan program Anda lagi dan amati tinggi dan volume yang berubah.
⇒ aquarium initialized
Width: 20 cm Length: 100 cm Height: 31 cm
Volume: 62 l
Width: 20 cm Length: 100 cm Height: 35 cm
Volume: 70 l
Sejauh ini belum ada pengubah visibilitas, seperti public
atau private
, dalam kode. Itu karena secara default, semua yang ada di Kotlin bersifat publik, yang berarti bahwa semuanya dapat diakses di mana saja, termasuk class, metode, properti, dan variabel anggota.
Di Kotlin, class, objek, antarmuka, konstruktor, fungsi, properti, dan penyetelnya dapat memiliki pengubah visibilitas:
public
berarti terlihat di luar class. Semuanya merupakan publik secara default, termasuk variabel dan metode class.internal
berarti elemen hanya akan terlihat dalam modul tersebut. Modul adalah sekumpulan file Kotlin yang dikompilasi bersama, misalnya, library atau aplikasi.private
berarti hanya akan terlihat di class tersebut (atau file sumber jika Anda bekerja dengan fungsi).protected
sama denganprivate
, tetapi juga akan terlihat di subclass mana pun.
Lihat Pengubah Visibilitas dalam dokumentasi Kotlin untuk informasi selengkapnya.
Variabel anggota
Properti dalam class atau variabel anggota adalah public
secara default. Jika Anda menentukannya dengan var
, parameter tersebut dapat diubah, yaitu dapat dibaca dan ditulis. Jika Anda menentukannya dengan val
, variabel tersebut bersifat hanya baca setelah inisialisasi.
Jika Anda menginginkan properti yang dapat dibaca atau ditulis oleh kode Anda, tetapi kode luar hanya dapat dibaca, Anda dapat membiarkan properti tersebut dan pengambilnya sebagai publik dan mendeklarasikan penyetel sebagai pribadi, seperti yang ditampilkan di bawah ini.
var volume: Int
get() = width * height * length / 1000
private set(value) {
height = (value * 1000) / (width * length)
}
Dalam tugas ini, Anda akan mempelajari cara kerja subclass dan turunan di Kotlin. Keduanya mirip dengan bahasa yang telah Anda lihat dalam bahasa lain, tetapi ada beberapa perbedaan.
Di Kotlin, secara default, class tidak dapat dibuatkan subclass-nya. Demikian pula, properti dan variabel anggota tidak dapat diganti oleh subclass (meskipun dapat diakses).
Anda harus menandai class sebagai open
agar dapat dibuatkan subclass-nya. Demikian pula, Anda harus menandai properti dan variabel anggota sebagai open
, untuk menggantinya di subclass. Kata kunci open
diperlukan untuk mencegah kebocoran detail implementasi secara tidak sengaja sebagai bagian dari antarmuka class.
Langkah 1: Buat kelas Akuarium terbuka
Pada langkah ini, Anda akan membuat class Aquarium
open
, sehingga Anda dapat menggantinya di langkah berikutnya.
- Tandai class
Aquarium
dan semua propertinya dengan kata kunciopen
.
open class Aquarium (open var length: Int = 100, open var width: Int = 20, open var height: Int = 40) {
open var volume: Int
get() = width * height * length / 1000
set(value) {
height = (value * 1000) / (width * length)
}
- Tambahkan properti
shape
terbuka dengan nilai"rectangle"
.
open val shape = "rectangle"
- Tambahkan properti
water
terbuka dengan pengambil yang menampilkan 90% volumeAquarium
.
open var water: Double = 0.0
get() = volume * 0.9
- Tambahkan kode ke metode
printSize()
untuk mencetak bentuk, dan jumlah air sebagai persentase volume.
fun printSize() {
println(shape)
println("Width: $width cm " +
"Length: $length cm " +
"Height: $height cm ")
// 1 l = 1000 cm^3
println("Volume: $volume l Water: $water l (${water/volume*100.0}% full)")
}
- Di
buildAquarium()
, ubah kode untuk membuatAquarium
denganwidth = 25
,length = 25
, danheight = 40
.
fun buildAquarium() {
val aquarium6 = Aquarium(length = 25, width = 25, height = 40)
aquarium6.printSize()
}
- Jalankan program Anda dan amati output baru.
⇒ aquarium initializing rectangle Width: 25 cm Length: 25 cm Height: 40 cm Volume: 25 l Water: 22.5 l (90.0% full)
Langkah 2: Buat subclass
- Buat subclass
Aquarium
yang disebutTowerTank
, yang menerapkan tangki silinder bulat, bukan tangki persegi panjang. Anda dapat menambahkanTowerTank
di bawahAquarium
, karena Anda dapat menambahkan class lain dalam file yang sama dengan classAquarium
. - Di
TowerTank
, ganti propertiheight
, yang ditentukan dalam konstruktor. Untuk mengganti properti, gunakan kata kuncioverride
di subclass.
- Membuat konstruktor untuk
TowerTank
mengambildiameter
. Gunakandiameter
untuklength
danwidth
saat memanggil konstruktor di superclassAquarium
.
class TowerTank (override var height: Int, var diameter: Int): Aquarium(height = height, width = diameter, length = diameter) {
- Ganti properti volume untuk menghitung silinder. Formula untuk silinder adalah pi yang dikalikan jari-jari pangkat dua kali tinggi. Anda perlu mengimpor konstanta
PI
darijava.lang.Math
.
override var volume: Int
// ellipse area = π * r1 * r2
get() = (width/2 * length/2 * height / 1000 * PI).toInt()
set(value) {
height = ((value * 1000 / PI) / (width/2 * length/2)).toInt()
}
- Di
TowerTank
, ganti propertiwater
menjadi 80% dari volume.
override var water = volume * 0.8
- Ganti
shape
menjadi"cylinder"
.
override val shape = "cylinder"
- Class
TowerTank
akhir akan terlihat seperti kode di bawah ini.
Aquarium.kt
:
package example.myapp
import java.lang.Math.PI
... // existing Aquarium class
class TowerTank (override var height: Int, var diameter: Int): Aquarium(height = height, width = diameter, length = diameter) {
override var volume: Int
// ellipse area = π * r1 * r2
get() = (width/2 * length/2 * height / 1000 * PI).toInt()
set(value) {
height = ((value * 1000 / PI) / (width/2 * length/2)).toInt()
}
override var water = volume * 0.8
override val shape = "cylinder"
}
- Di
buildAquarium()
, buatTowerTank
dengan diameter 25 cm dan tinggi 45 cm. Cetak ukurannya.
main.kt:
package example.myapp
fun buildAquarium() {
val myAquarium = Aquarium(width = 25, length = 25, height = 40)
myAquarium.printSize()
val myTower = TowerTank(diameter = 25, height = 40)
myTower.printSize()
}
- Jalankan program Anda dan amati outputnya.
⇒ aquarium initializing rectangle Width: 25 cm Length: 25 cm Height: 40 cm Volume: 25 l Water: 22.5 l (90.0% full) aquarium initializing cylinder Width: 25 cm Length: 25 cm Height: 40 cm Volume: 18 l Water: 14.4 l (80.0% full)
Terkadang Anda ingin menentukan perilaku atau properti umum untuk dibagikan di antara beberapa class terkait. Kotlin menawarkan dua cara untuk melakukannya, yaitu antarmuka dan class abstrak. Dalam tugas ini, Anda membuat class AquariumFish
abstrak untuk properti yang umum untuk semua ikan. Anda membuat antarmuka FishAction
untuk menentukan perilaku yang umum pada semua ikan.
- Baik class abstrak maupun antarmuka tidak dapat dibuat instance-nya sendiri, yang berarti Anda tidak dapat membuat objek jenis tersebut secara langsung.
- Class abstrak memiliki konstruktor.
- Antarmuka tidak boleh memiliki logika konstruktor atau menyimpan status apa pun.
Langkah 1. Membuat class abstrak
- Pada example.myapp, buat file baru,
AquariumFish.kt
. - Buat class, juga disebut
AquariumFish
, dan tandai denganabstract
. - Tambahkan satu properti
String
,color
, dan tandai denganabstract
.
package example.myapp
abstract class AquariumFish {
abstract val color: String
}
- Buat dua subclass dari
AquariumFish
,Shark
, danPlecostomus
. - Karena
color
bersifat abstrak, subclass harus menerapkannya. JadikanShark
berwarna abu-abu danPlecostomus
emas.
class Shark: AquariumFish() {
override val color = "gray"
}
class Plecostomus: AquariumFish() {
override val color = "gold"
}
- Di main.kt, buat fungsi
makeFish()
untuk menguji class Anda. Buat instanceShark
danPlecostomus
, lalu cetak warna masing-masing. - Hapus kode pengujian Anda sebelumnya di
main()
dan tambahkan panggilan kemakeFish()
. Kodenya akan terlihat seperti berikut ini.
main.kt
:
package example.myapp
fun makeFish() {
val shark = Shark()
val pleco = Plecostomus()
println("Shark: ${shark.color}")
println("Plecostomus: ${pleco.color}")
}
fun main () {
makeFish()
}
- Jalankan program Anda dan amati outputnya.
⇒ Shark: gray Plecostomus: gold
Diagram berikut mewakili class Shark
dan class Plecostomus
, yang membuat subclass untuk class abstrak, AquariumFish
.
Langkah 2. Membuat antarmuka
- Di AquariumFish.kt, buat antarmuka yang disebut
FishAction
dengan metodeeat()
.
interface FishAction {
fun eat()
}
- Tambahkan
FishAction
ke setiap subclass, dan implementasikaneat()
agar dapat mencetak fungsi ikan.
class Shark: AquariumFish(), FishAction {
override val color = "gray"
override fun eat() {
println("hunt and eat fish")
}
}
class Plecostomus: AquariumFish(), FishAction {
override val color = "gold"
override fun eat() {
println("eat algae")
}
}
- Dalam fungsi
makeFish()
, minta setiap ikan yang Anda buat akan makan sesuatu dengan memanggileat()
.
fun makeFish() {
val shark = Shark()
val pleco = Plecostomus()
println("Shark: ${shark.color}")
shark.eat()
println("Plecostomus: ${pleco.color}")
pleco.eat()
}
- Jalankan program Anda dan amati outputnya.
⇒ Shark: gray hunt and eat fish Plecostomus: gold eat algae
Diagram berikut mewakili class Shark
dan class Plecostomus
, yang keduanya terdiri dari dan mengimplementasikan antarmuka FishAction
.
Kapan class abstrak versus antarmuka digunakan
Contoh di atas sederhana, tetapi jika Anda memiliki banyak class yang saling terkait, class dan antarmuka abstrak dapat membantu membuat desain Anda lebih rapi, lebih teratur, dan lebih mudah dikelola.
Seperti disebutkan di atas, class abstrak dapat memiliki konstruktor, dan antarmuka tidak bisa, tetapi sangat mirip. Jadi, kapan sebaiknya Anda menggunakan keduanya?
Ketika Anda menggunakan antarmuka untuk menulis class, fungsionalitas class diperluas melalui instance class yang dikandungnya. Komposisi cenderung membuat kode lebih mudah digunakan kembali dan memberikan alasan daripada pewarisan dari class abstrak. Selain itu, Anda dapat menggunakan beberapa antarmuka di class, tetapi Anda hanya dapat membuat subclass dari satu class abstrak.
Komposisi sering kali menghasilkan enkapsulasi yang lebih baik, penggabungan (interdependensi) yang lebih rendah, antarmuka yang lebih rapi, dan kode yang lebih bermanfaat. Untuk alasan ini, penggunaan komposisi dengan antarmuka adalah desain yang disukai. Di sisi lain, pewarisan dari class abstrak cenderung lebih cocok untuk beberapa masalah. Jadi, Anda harus memilih komposisi, tetapi ketika pewarisan masuk akal, Kotlin juga memungkinkan Anda melakukannya!
- Gunakan antarmuka jika Anda memiliki banyak metode dan satu atau dua penerapan default, misalnya seperti pada
AquariumAction
di bawah.
interface AquariumAction {
fun eat()
fun jump()
fun clean()
fun catchFish()
fun swim() {
println("swim")
}
}
- Gunakan kelas abstrak kapan pun Anda tidak dapat menyelesaikan kelas. Misalnya, dengan kembali ke class
AquariumFish
, Anda dapat membuat semuaAquariumFish
mengimplementasikanFishAction
, dan menyediakan implementasi default untukeat
sekaligus membiarkancolor
abstrak, karena tidak ada warna default untuk ikan.
interface FishAction {
fun eat()
}
abstract class AquariumFish: FishAction {
abstract val color: String
override fun eat() = println("yum")
}
Tugas sebelumnya memperkenalkan class abstrak, antarmuka, dan ide komposisi. Delegasi antarmuka adalah teknik lanjutan dengan metode antarmuka yang diterapkan oleh objek helper (atau delegasi), yang kemudian digunakan oleh class. Teknik ini dapat berguna saat Anda menggunakan antarmuka dalam serangkaian class yang tidak terkait: Anda menambahkan fungsi antarmuka yang diperlukan ke class helper yang terpisah, dan setiap class menggunakan instance class helper untuk menerapkan fungsi.
Dalam tugas ini, Anda menggunakan delegasi antarmuka untuk menambahkan fungsi ke class.
Langkah 1: Buat antarmuka baru
- Di AquariumFish.kt, hapus class
AquariumFish
. Daripada mewarisi dari classAquariumFish
,Plecostomus
danShark
akan mengimplementasikan antarmuka untuk tindakan ikan dan warnanya. - Buat antarmuka baru,
FishColor
, yang menentukan warna sebagai string.
interface FishColor {
val color: String
}
- Mengubah
Plecostomus
untuk mengimplementasikan dua antarmuka,FishAction
, danFishColor
. Anda perlu mengganticolor
dariFishColor
daneat()
dariFishAction
.
class Plecostomus: FishAction, FishColor {
override val color = "gold"
override fun eat() {
println("eat algae")
}
}
- Ubah class
Shark
Anda untuk menerapkan juga dua antarmuka,FishAction
danFishColor
, bukan mewarisi dariAquariumFish
.
class Shark: FishAction, FishColor {
override val color = "gray"
override fun eat() {
println("hunt and eat fish")
}
}
- Kode yang sudah selesai akan terlihat seperti ini:
package example.myapp
interface FishAction {
fun eat()
}
interface FishColor {
val color: String
}
class Plecostomus: FishAction, FishColor {
override val color = "gold"
override fun eat() {
println("eat algae")
}
}
class Shark: FishAction, FishColor {
override val color = "gray"
override fun eat() {
println("hunt and eat fish")
}
}
Langkah 2: Buat class singleton
Selanjutnya, Anda akan mengimplementasikan penyiapan untuk bagian delegasi dengan membuat class helper yang mengimplementasikan FishColor
. Anda akan membuat class dasar bernama GoldColor
yang mengimplementasikan FishColor
—yang hanya mengatakan bahwa warnanya adalah emas.
Tidak masuk akal untuk membuat beberapa instance GoldColor
, karena semuanya melakukan hal yang sama persis. Jadi, Kotlin memungkinkan Anda mendeklarasikan class tempat Anda hanya dapat membuat satu instance dengan menggunakan kata kunci object
, bukan class
. Kotlin akan membuat satu instance tersebut, dan instance tersebut direferensikan oleh nama class. Lalu semua objek lainnya dapat menggunakan instance ini saja, tidak ada cara untuk membuat instance class ini. Jika Anda sudah terbiasa dengan pola singleton, ini adalah cara Anda menerapkan singleton di Kotlin.
- Di AquariumFish.kt, buat objek untuk
GoldColor
. Mengganti warna.
object GoldColor : FishColor {
override val color = "gold"
}
Langkah 3: Tambahkan delegasi antarmuka untuk FishColor
Sekarang Anda siap untuk menggunakan delegasi antarmuka.
- Di AquariumFish.kt, hapus penggantian
color
dariPlecostomus
. - Ubah class
Plecostomus
untuk mendapatkan warnanya dariGoldColor
. Untuk melakukannya, tambahkanby GoldColor
ke deklarasi class, lalu buat delegasi. Artinya, daripada menerapkanFishColor
, gunakan implementasi yang disediakan olehGoldColor
. Jadi, setiap kali diakses,color
akan didelegasikan keGoldColor
.
class Plecostomus: FishAction, FishColor by GoldColor {
override fun eat() {
println("eat algae")
}
}
Dengan kelas apa adanya, semua Plecos akan berwarna emas, tetapi ikan ini sebenarnya datang dalam berbagai warna. Anda dapat mengatasinya dengan menambahkan parameter konstruktor untuk warna dengan GoldColor
sebagai warna default untuk Plecostomus
.
- Ubah class
Plecostomus
untuk menerimafishColor
di konstruktornya, dan setel default-nya keGoldColor
. Ubah delegasi dariby GoldColor
menjadiby fishColor
.
class Plecostomus(fishColor: FishColor = GoldColor): FishAction,
FishColor by fishColor {
override fun eat() {
println("eat algae")
}
}
Langkah 4: Tambahkan delegasi antarmuka untuk FishAction
Dengan cara yang sama, Anda dapat menggunakan delegasi antarmuka untuk FishAction
.
- Dalam AquariumFish.kt, buat class
PrintingFishAction
yang mengimplementasikanFishAction
, yang mengambilString
,food
, lalu mencetak apa yang dimakan ikan.
class PrintingFishAction(val food: String) : FishAction {
override fun eat() {
println(food)
}
}
- Di class
Plecostomus
, hapus fungsi penggantianeat()
, karena Anda akan menggantinya dengan delegasi. - Pada deklarasi
Plecostomus
, delegasikanFishAction
kePrintingFishAction
, yang meneruskan"eat algae"
. - Dengan semua delegasi tersebut, tidak ada kode dalam isi class
Plecostomus
, jadi hapus{}
, karena semua penggantian ditangani oleh delegasi antarmuka
class Plecostomus (fishColor: FishColor = GoldColor):
FishAction by PrintingFishAction("eat algae"),
FishColor by fishColor
Diagram berikut mewakili class Shark
dan Plecostomus
, keduanya terdiri dari antarmuka PrintingFishAction
dan FishColor
, tetapi mendelegasikan implementasi untuknya.
Delegasi antarmuka sangat kuat, dan secara umum Anda harus mempertimbangkan cara menggunakannya setiap kali menggunakan class abstrak dalam bahasa lain. Alat ini memungkinkan Anda menggunakan komposisi untuk memasukkan perilaku, tanpa memerlukan banyak subclass, yang masing-masing memiliki cara berbeda.
Class data mirip dengan struct
di beberapa bahasa lain—class ini utamanya ada untuk menyimpan beberapa data—tetapi objek class data masih menjadi objek. Objek class data Kotlin memiliki beberapa manfaat tambahan, seperti utilitas untuk mencetak dan menyalin. Dalam tugas ini, Anda akan membuat class data sederhana dan mempelajari dukungan yang disediakan Kotlin untuk class data.
Langkah 1: Buat class data
- Tambahkan paket baru
decor
di bagian paket example.myapp untuk menyimpan kode baru. Klik kanan example.myapp di panel Project, lalu pilih File > New > Package. - Dalam paket, buat class baru bernama
Decoration
.
package example.myapp.decor
class Decoration {
}
- Untuk membuat
Decoration
class data, awali deklarasi class dengan kata kuncidata
. - Tambahkan properti
String
yang disebutrocks
untuk memberikan data ke class.
data class Decoration(val rocks: String) {
}
- Di dalam file, di luar class, tambahkan fungsi
makeDecorations()
untuk membuat dan mencetak instanceDecoration
dengan"granite"
.
fun makeDecorations() {
val decoration1 = Decoration("granite")
println(decoration1)
}
- Tambahkan fungsi
main()
untuk memanggilmakeDecorations()
, dan jalankan program Anda. Perhatikan output logis yang dibuat karena ini adalah class data.
⇒ Decoration(rocks=granite)
- Di
makeDecorations()
, buat instance dua objekDecoration
lagi yang keduanya merupakan "slate" lalu mencetaknya.
fun makeDecorations() {
val decoration1 = Decoration("granite")
println(decoration1)
val decoration2 = Decoration("slate")
println(decoration2)
val decoration3 = Decoration("slate")
println(decoration3)
}
- Di
makeDecorations()
, tambahkan pernyataan cetak yang mencetak hasil perbandingandecoration1
dengandecoration2
, dan pernyataan kedua yang membandingkandecoration3
dengandecoration2
. Gunakan metode equals() yang disediakan oleh class data.
println (decoration1.equals(decoration2))
println (decoration3.equals(decoration2))
- Jalankan kode.
⇒ Decoration(rocks=granite) Decoration(rocks=slate) Decoration(rocks=slate) false true
Langkah 2. Menggunakan destrukturisasi
Untuk mendapatkan properti objek data dan menetapkannya ke variabel, Anda dapat menetapkannya satu per satu, seperti ini.
val rock = decoration.rock
val wood = decoration.wood
val diver = decoration.diver
Sebagai gantinya, Anda dapat membuat variabel, satu untuk setiap properti, dan menetapkan objek data ke grup variabel. Kotlin menempatkan nilai properti di setiap variabel.
val (rock, wood, diver) = decoration
Ini disebut destrukturisasi dan merupakan singkatan yang berguna. Jumlah variabel harus cocok dengan jumlah properti, dan variabel ditetapkan sesuai urutan deklarasinya di class tersebut. Berikut adalah contoh lengkap yang dapat Anda coba di Decoration.kt.
// Here is a data class with 3 properties.
data class Decoration2(val rocks: String, val wood: String, val diver: String){
}
fun makeDecorations() {
val d5 = Decoration2("crystal", "wood", "diver")
println(d5)
// Assign all properties to variables.
val (rock, wood, diver) = d5
println(rock)
println(wood)
println(diver)
}
⇒ Decoration2(rocks=crystal, wood=wood, diver=diver) crystal wood diver
Jika tidak memerlukan satu atau beberapa properti, Anda dapat melewatinya menggunakan _
, bukan nama variabel, seperti yang ditunjukkan pada kode di bawah ini.
val (rock, _, diver) = d5
Dalam tugas ini, Anda akan mempelajari beberapa class dengan tujuan khusus di Kotlin, termasuk yang berikut:
- Class singleton
- Enumerasi
- Kelas tertutup
Langkah 1: Tarik kembali class singleton
Ingat kembali contoh sebelumnya dengan class GoldColor
.
object GoldColor : FishColor {
override val color = "gold"
}
Karena setiap instance GoldColor
melakukan hal yang sama, instance tersebut dideklarasikan sebagai object
, bukan sebagai class
untuk menjadikannya singleton. Hanya boleh ada satu instance.
Langkah 2: Buat enum
Kotlin juga mendukung enumerasi, yang memungkinkan Anda menghitung sesuatu dan merujuknya berdasarkan nama, sama seperti dalam bahasa lain. Deklarasikan enum dengan mengawali awalan dengan kata kunci enum
. Deklarasi enum dasar hanya memerlukan daftar nama, namun Anda juga dapat menentukan satu atau beberapa kolom yang terkait dengan setiap nama.
- Dalam Decoration.kt, coba contoh enum.
enum class Color(val rgb: Int) {
RED(0xFF0000), GREEN(0x00FF00), BLUE(0x0000FF);
}
Enum sedikit mirip dengan singleton—hanya boleh ada satu, dan hanya satu dari setiap nilai dalam enumerasi. Misalnya, hanya boleh ada satu Color.RED
, satu Color.GREEN
, dan satu Color.BLUE
. Dalam contoh ini, nilai RGB ditetapkan ke properti rgb
untuk mewakili komponen warna. Anda juga bisa mendapatkan nilai ordinal enum menggunakan properti ordinal
, dan namanya menggunakan properti name
.
- Coba contoh lain enum.
enum class Direction(val degrees: Int) {
NORTH(0), SOUTH(180), EAST(90), WEST(270)
}
fun main() {
println(Direction.EAST.name)
println(Direction.EAST.ordinal)
println(Direction.EAST.degrees)
}
⇒ EAST 2 90
Langkah 3: Buat class tertutup
Class tertutup adalah class yang dapat dibuat subclass, tetapi hanya di dalam file tempatnya dideklarasikan. Jika Anda mencoba membuat subclass untuk class di file yang berbeda, Anda akan mendapatkan error.
Karena class dan subclass berada di file yang sama, Kotlin akan mengetahui semua subclass secara statis. Artinya, pada waktu kompilasi, compiler melihat semua class dan subclass dan mengetahui bahwa ini adalah semuanya, sehingga compiler dapat melakukan pemeriksaan tambahan untuk Anda.
- Di AquariumFish.kt, cobalah contoh class tertutup, agar sesuai dengan tema akuatik.
sealed class Seal
class SeaLion : Seal()
class Walrus : Seal()
fun matchSeal(seal: Seal): String {
return when(seal) {
is Walrus -> "walrus"
is SeaLion -> "sea lion"
}
}
Class Seal
tidak dapat dibuatkan subclass-nya dalam file lain. Jika ingin menambahkan lebih banyak jenis Seal
, Anda harus menambahkannya dalam file yang sama. Oleh karena itu, class tertutup menjadi cara yang aman untuk mewakili jenis tertentu. Misalnya, class tertutup cocok untuk menampilkan keberhasilan atau error dari API jaringan.
Pelajaran ini membahas banyak hal. Meskipun banyak dari elemen tersebut seharusnya sudah dikenal dari bahasa pemrograman berorientasi objek lainnya, Kotlin menambahkan beberapa fitur untuk menjaga kode tetap ringkas dan mudah dibaca.
Class dan konstruktor
- Tentukan class di Kotlin menggunakan
class
. - Kotlin secara otomatis membuat penyetel dan pengambil untuk properti.
- Menentukan konstruktor utama secara langsung dalam definisi class. Contoh:
class Aquarium(var length: Int = 100, var width: Int = 20, var height: Int = 40)
- Jika konstruktor utama memerlukan kode tambahan, tulis kode tersebut dalam satu atau beberapa blok
init
. - Class dapat menentukan satu atau beberapa konstruktor sekunder menggunakan
constructor
, tetapi gaya Kotlin adalah menggunakan fungsi factory sebagai gantinya.
Pengubah visibilitas dan subclass
- Semua class dan fungsi di Kotlin secara default ditetapkan ke
public
, tetapi Anda dapat menggunakan pengubah untuk mengubah visibilitas keinternal
,private
, atauprotected
. - Untuk membuat subclass, class induk harus ditandai sebagai
open
. - Untuk mengganti metode dan properti di subclass, metode dan properti harus ditandai
open
di class induk. - Class tertutup hanya dapat dibuat subclass di file yang sama tempatnya ditentukan. Buat class tertutup dengan mengawali deklarasi dengan
sealed
.
Class data, singleton, dan enum
- Buat class data dengan menambahkan awalan ke
data
di deklarasi. - Destrukturisasi adalah singkatan untuk menetapkan properti objek
data
ke variabel terpisah. - Buat class singleton dengan menggunakan
object
bukanclass
. - Tentukan enum menggunakan
enum class
.
Class, antarmuka, dan delegasi abstrak
- Class dan antarmuka abstrak adalah dua cara untuk berbagi perilaku umum di antara class.
- Class abstrak menentukan properti dan perilaku, tetapi menerapkan implementasinya ke subclass.
- Antarmuka menentukan perilaku, dan dapat menyediakan implementasi default untuk sebagian atau semua perilaku.
- Ketika Anda menggunakan antarmuka untuk menulis class, fungsionalitas class diperluas melalui instance class yang dikandungnya.
- Delegasi antarmuka menggunakan komposisi, tetapi juga mendelegasikan implementasi ke class antarmuka.
- Komposisi adalah cara yang efektif untuk menambahkan fungsi ke class menggunakan delegasi antarmuka. Dalam komposisi umum lebih disarankan, tetapi pewarisan dari class abstrak lebih cocok untuk beberapa masalah.
Dokumentasi Kotlin
Jika Anda menginginkan informasi lebih lanjut tentang setiap topik dalam kursus ini, atau jika mengalami kesulitan, https://kotlinlang.org adalah langkah awal yang terbaik.
- Class dan pewarisan
- Konstruktor
- Fungsi factory
- Properti dan kolom
- Pengubah visibilitas
- Class abstrak
- Antarmuka
- Delegasi
- Class data
- Persamaan
- Destrukturisasi
- Deklarasi objek
- Class enum
- Kelas tertutup
- Menangani Error Opsional Menggunakan Class Segel Kotlin
Tutorial Kotlin
Situs https://try.kotlinlang.org menyertakan berbagai tutorial yang disebut Kotlin Koans, penafsir berbasis web, dan kumpulan lengkap dokumentasi referensi dengan contoh.
Kursus Udacity
Untuk melihat kursus Udacity tentang topik ini, lihat Bootcamp Kotlin untuk Pemrogram
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. Terserah instruktur untuk melakukan hal berikut:
- Tugaskan pekerjaan rumah jika diperlukan.
- Berkomunikasi dengan siswa cara mengirimkan tugas pekerjaan rumah.
- Beri nilai tugas pekerjaan rumah.
Instruktur dapat menggunakan saran ini sesedikit atau sebanyak yang mereka inginkan, dan harus bebas memberikan pekerjaan rumah lain yang dirasa sesuai.
Jika Anda mengerjakan codelab ini sendiri, silakan gunakan tugas pekerjaan rumah ini untuk menguji pengetahuan Anda.
Jawab pertanyaan berikut
Pertanyaan 1
Class memiliki metode khusus yang berfungsi sebagai blueprint untuk membuat objek dari class tersebut. Metode apa yang digunakan?
▢ Builder
▢ Instance
▢ Konstruktor
▢ Cetak biru
Pertanyaan 2
Manakah dari pernyataan tentang antarmuka dan class abstrak berikut yang TIDAK benar?
▢ Class abstrak dapat memiliki konstruktor.
▢ Antarmuka tidak boleh memiliki konstruktor.
▢ Antarmuka dan class abstrak dapat dibuat instance secara langsung.
▢ Properti abstrak harus diimplementasikan oleh subclass dari class abstrak.
Pertanyaan 3
Manakah dari berikut ini yang BUKAN merupakan pengubah visibilitas Kotlin untuk properti, metode, dll.?
▢ internal
▢ nosubclass
▢ protected
▢ private
Pertanyaan 4
Perhatikan class data ini:data class Fish(val name: String, val species:String, val colors:String)
Manakah dari kode berikut yang BUKAN kode yang valid untuk membuat dan membatalkan struktur objek Fish
?
▢ val (name1, species1, colors1) = Fish("Pat", "Plecostomus", "gold")
▢ val (name2, _, colors2) = Fish("Bitey", "shark", "gray")
▢ val (name3, species3, _) = Fish("Amy", "angelfish", "blue and black stripes")
▢ val (name4, species4, colors4) = Fish("Harry", "halibut")
Pertanyaan 5
Misalnya Anda memiliki kebun binatang dengan banyak hewan yang harus dipelihara. Manakah dari berikut ini yang BUKAN merupakan bagian dari penerapan perawatan?
▢ interface
untuk berbagai jenis makanan yang dimakan hewan.
▢ Class abstract Caretaker
tempat Anda dapat membuat berbagai jenis pengasuh.
▢ interface
untuk menyediakan air bersih bagi hewan.
▢ Class data
untuk entri dalam jadwal pemberian makan.
Lanjutkan ke pelajaran berikutnya:
Untuk ringkasan kursus, termasuk link ke codelab lainnya, lihat "Kotlin Bootcamp for Programmers: Selamat datang di kursus."