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 membuat program Kotlin, lalu mempelajari class dan objek di Kotlin. Sebagian besar konten ini akan terasa tidak asing bagi Anda jika Anda menguasai bahasa berorientasi objek lain, tetapi Kotlin memiliki beberapa perbedaan penting untuk mengurangi jumlah kode yang perlu Anda tulis. Anda juga akan mempelajari class abstrak dan delegasi antarmuka.
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
- Dasar-dasar Kotlin, termasuk jenis, operator, dan perulangan
- 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 pewarisan
- 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
- Periksa contoh class abstrak dan antarmuka
- Membuat class data sederhana
- Mempelajari singleton, enum, dan class tertutup
Istilah pemrograman berikut seharusnya sudah tidak asing bagi Anda:
- Class adalah cetak biru untuk objek. Misalnya, class
Aquarium
adalah blueprint untuk membuat objek akuarium. - Objek adalah instance class; objek akuarium adalah salah satu
Aquarium
aktual. - Properti adalah karakteristik class, seperti panjang, lebar, dan tinggi
Aquarium
. - Metode, yang juga disebut fungsi anggota, adalah fungsi class. Metode adalah hal yang dapat Anda "lakukan" dengan objek. Misalnya, Anda dapat
fillWithWater()
objekAquarium
. - Antarmuka adalah spesifikasi yang dapat diimplementasikan oleh class. Misalnya, pembersihan umum untuk objek selain akuarium, dan pembersihan umumnya terjadi dengan cara yang serupa untuk objek yang berbeda. Jadi, Anda dapat 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 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 mengatur kode.
- Di panel Project, di project Hello Kotlin, klik kanan folder src.
- Pilih New > Package, lalu beri nama
example.myapp
.
Langkah 2: Buat class dengan properti
Class ditentukan 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 lakukan 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.
- Di dropdown Jenis, biarkan pilihan sebagai File, dan beri nama file
main.kt
. IntelliJ IDEA menyertakan nama paket, tetapi tidak menyertakan definisi class untuk file. - Tentukan fungsi
buildAquarium()
dan di dalamnya buat instanceAquarium
. Untuk membuat instance, referensikan class seolah-olah itu adalah fungsi,Aquarium()
. Tindakan ini 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 menyetel tinggi menjadi 60 dan mencetak properti dimensi yang diubah.
fun buildAquarium() {
val myAquarium = Aquarium()
myAquarium.printSize()
myAquarium.height = 60
myAquarium.printSize()
}
- Jalankan program Anda dan amati output-nya.
⇒ 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 melanjutkan pekerjaan dengan properti.
Langkah 1: Buat konstruktor
Pada langkah ini, Anda akan menambahkan konstruktor ke class Aquarium
yang Anda buat pada tugas pertama. Dalam contoh sebelumnya, setiap instance Aquarium
dibuat dengan dimensi yang sama. Anda dapat mengubah dimensi setelah dibuat dengan menetapkan properti, tetapi akan lebih mudah untuk membuat ukuran yang tepat sejak awal.
Dalam beberapa bahasa pemrograman, konstruktor ditentukan dengan membuat metode dalam class yang memiliki nama yang sama dengan class. Di Kotlin, Anda menentukan konstruktor langsung dalam deklarasi class itu sendiri, dengan menentukan parameter di dalam tanda kurung seolah-olah class adalah 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
, lalu 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 dengan menentukan properti secara langsung dengan konstruktor, menggunakan
var
atauval
, dan Kotlin juga membuat getter dan setter secara otomatis. Kemudian, Anda dapat menghapus definisi properti di 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 di antaranya, atau menentukan semuanya dan membuatAquarium
berukuran sepenuhnya kustom. Pada fungsibuildAquarium()
, coba berbagai cara untuk 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 output-nya.
⇒ 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 perlu membebani konstruktor dan menulis versi yang berbeda untuk setiap kasus ini (plus beberapa lagi untuk kombinasi lainnya). Kotlin membuat apa yang diperlukan dari nilai default dan parameter bernama.
Langkah 2: Tambahkan blok init
Konstruktor contoh di atas hanya mendeklarasikan properti dan menetapkan nilai ekspresi ke properti tersebut. Jika konstruktor Anda memerlukan lebih banyak kode inisialisasi, kode tersebut dapat ditempatkan dalam satu atau beberapa blok init
. Pada langkah ini, Anda akan 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 output-nya.
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
dijalankan 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 kelas 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 mengambil sejumlah ikan sebagai argumennya, menggunakan kata kunciconstructor
. Buat properti tangkival
untuk volume akuarium yang dihitung dalam liter berdasarkan jumlah ikan. Asumsikan 2 liter (2.000 cm^3) air per ikan, ditambah 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 ditetapkan dalam konstruktor utama) yang sama, dan hitung tinggi yang diperlukan untuk membuat tangki dengan volume yang ditentukan.
// calculate the height needed
height = (tank / (length * width)).toInt()
- Pada fungsi
buildAquarium()
, tambahkan panggilan untuk membuatAquarium
menggunakan konstruktor sekunder baru Anda. 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 output-nya.
⇒ 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 dieksekusi, dan sekali oleh kode di buildAquarium()
.
Anda juga dapat menyertakan kata kunci constructor
di konstruktor utama, tetapi hal ini tidak diperlukan dalam sebagian besar kasus.
Langkah 4: Tambahkan pengambil properti baru
Pada langkah ini, Anda akan menambahkan getter 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 untuknya. Karena volume
perlu dihitung, pengambil perlu 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 di
buildAquarium()
yang mencetak volume. - Dalam 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 output-nya.
⇒ aquarium initializing Width: 20 cm Length: 100 cm Height: 31 cm Volume: 62 l
Dimensi dan volume sama seperti sebelumnya, tetapi volume hanya dicetak satu kali setelah objek diinisialisasi sepenuhnya oleh konstruktor utama dan konstruktor sekunder.
Langkah 5: Tambahkan setter properti
Pada langkah ini, Anda membuat setter properti baru untuk volume.
- Di class
Aquarium
, ubahvolume
menjadivar
sehingga dapat ditetapkan lebih dari sekali. - Tambahkan setter untuk properti
volume
dengan menambahkan metodeset()
di bawah getter, yang menghitung ulang tinggi berdasarkan jumlah air yang diberikan. Menurut konvensi, nama parameter setter adalahvalue
, tetapi Anda dapat mengubahnya jika mau.
var volume: Int
get() = width * height * length / 1000
set(value) {
height = (value * 1000) / (width * length)
}
- Di
buildAquarium()
, tambahkan kode untuk menyetel volume Akuarium menjadi 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 perubahan tinggi dan volume.
⇒ 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. Hal ini karena secara default, semua hal di Kotlin bersifat publik, yang berarti semua hal 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 ke luar class. Semuanya merupakan publik secara default, termasuk variabel dan metode class.internal
berarti hanya akan terlihat dalam modul tersebut. Modul adalah sekumpulan file Kotlin yang dikompilasi bersama, misalnya, library atau aplikasi.private
berarti hanya akan terlihat di dalam class (atau file sumber jika Anda bekerja dengan fungsi).protected
sama sepertiprivate
, tetapi juga akan terlihat di subclass mana pun.
Lihat Pengubah Visibilitas dalam dokumentasi Kotlin untuk mengetahui informasi selengkapnya.
Variabel anggota
Properti dalam class, atau variabel anggota, adalah public
secara default. Jika Anda menentukannya dengan var
, class data tersebut dapat diubah, yaitu dapat dibaca dan ditulis. Jika Anda menentukannya dengan val
, nilai tersebut bersifat hanya baca setelah inisialisasi.
Jika Anda menginginkan properti yang dapat dibaca atau ditulis oleh kode Anda, tetapi hanya dapat dibaca oleh kode di luar, Anda dapat membiarkan properti dan getter-nya sebagai publik serta mendeklarasikan setter-nya sebagai pribadi, seperti yang ditunjukkan di bawah.
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 pewarisan di Kotlin. Tampilannya mirip dengan yang Anda lihat dalam bahasa lain, tetapi ada beberapa perbedaan.
Di Kotlin, secara default, class tidak dapat dibuat subclass. Demikian pula, properti dan variabel anggota tidak dapat diganti oleh subclass (meskipun dapat diakses).
Anda harus menandai class sebagai open
agar dapat dibuat 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: Buka kelas Akuarium
Pada langkah ini, Anda akan membuat class Aquarium
menjadi open
, sehingga Anda dapat menggantinya pada 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 getter 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
bernamaTowerTank
, yang mengimplementasikan 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.
- Buat 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. Rumus untuk tabung adalah pi kali 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 Anda akan terlihat seperti kode di bawah.
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 ukuran.
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 output-nya.
⇒ 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 yang akan dibagikan di antara beberapa class terkait. Kotlin menawarkan dua cara untuk melakukannya, yaitu antarmuka dan class abstrak. Dalam tugas ini, Anda akan membuat class AquariumFish
abstrak untuk properti yang umum untuk semua ikan. Anda membuat antarmuka yang disebut FishAction
untuk menentukan perilaku umum semua ikan.
- 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 dapat memiliki logika konstruktor atau menyimpan status apa pun.
Langkah 1. Membuat class abstrak
- Di bagian example.myapp, buat file baru,
AquariumFish.kt
. - Buat class, yang 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
AquariumFish
,Shark
, danPlecostomus
. - Karena
color
bersifat abstrak, subclass harus menerapkannya. BuatShark
berwarna abu-abu danPlecostomus
berwarna 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 sebelumnya di
main()
dan tambahkan panggilan kemakeFish()
. Kode Anda akan terlihat seperti kode di bawah.
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 output-nya.
⇒ Shark: gray Plecostomus: gold
Diagram berikut merepresentasikan class Shark
dan class Plecostomus
, yang merupakan subclass dari class abstrak, AquariumFish
.
Langkah 2. Membuat antarmuka
- Di AquariumFish.kt, buat antarmuka bernama
FishAction
dengan metodeeat()
.
interface FishAction {
fun eat()
}
- Tambahkan
FishAction
ke setiap subclass, dan terapkaneat()
dengan membuatnya mencetak apa yang dilakukan 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")
}
}
- Di fungsi
makeFish()
, buat setiap ikan yang Anda buat memakan 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 output-nya.
⇒ Shark: gray hunt and eat fish Plecostomus: gold eat algae
Diagram berikut menampilkan class Shark
dan class Plecostomus
, yang keduanya terdiri dari dan menerapkan antarmuka FishAction
.
Kapan harus menggunakan class abstrak versus antarmuka
Contoh di atas sederhana, tetapi jika Anda memiliki banyak class yang saling terkait, class abstrak dan antarmuka dapat membantu Anda menjaga desain tetap bersih, lebih teratur, dan lebih mudah dikelola.
Seperti yang disebutkan di atas, class abstrak dapat memiliki konstruktor, dan antarmuka tidak dapat, tetapi keduanya sangat mirip. Jadi, kapan Anda harus menggunakan masing-masingnya?
Saat Anda menggunakan antarmuka untuk menyusun class, fungsi class diperluas melalui instance class yang dikandungnya. Komposisi cenderung membuat kode lebih mudah digunakan kembali dan dipahami daripada pewarisan dari class abstrak. Selain itu, Anda dapat menggunakan beberapa antarmuka dalam satu class, tetapi Anda hanya dapat membuat subclass dari satu class abstrak.
Komposisi sering kali menghasilkan enkapsulasi yang lebih baik, coupling (interdependensi) yang lebih rendah, antarmuka yang lebih bersih, dan kode yang lebih mudah digunakan. Karena alasan ini, menggunakan komposisi dengan antarmuka adalah desain yang lebih disukai. Di sisi lain, pewarisan dari class abstrak cenderung cocok secara alami untuk beberapa masalah. Jadi, Anda harus lebih memilih komposisi, tetapi jika pewarisan masuk akal, Kotlin juga memungkinkan Anda melakukannya.
- Gunakan antarmuka jika Anda memiliki banyak metode dan satu atau dua implementasi default, misalnya seperti pada
AquariumAction
di bawah.
interface AquariumAction {
fun eat()
fun jump()
fun clean()
fun catchFish()
fun swim() {
println("swim")
}
}
- Gunakan class abstrak setiap kali Anda tidak dapat menyelesaikan class. Misalnya, dengan kembali ke class
AquariumFish
, Anda dapat membuat semuaAquariumFish
menerapkanFishAction
, dan memberikan implementasi default untukeat
sambil membiarkancolor
abstrak, karena sebenarnya 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 di mana metode antarmuka diimplementasikan 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 terpisah, dan setiap class menggunakan instance class helper untuk mengimplementasikan 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 menerapkan antarmuka untuk tindakan ikan dan warnanya. - Buat antarmuka baru,
FishColor
, yang menentukan warna sebagai string.
interface FishColor {
val color: String
}
- Ubah
Plecostomus
untuk menerapkan dua antarmuka,FishAction
, danFishColor
. Anda harus mengganticolor
dariFishColor
daneat()
dariFishAction
.
class Plecostomus: FishAction, FishColor {
override val color = "gold"
override fun eat() {
println("eat algae")
}
}
- Ubah class
Shark
Anda agar juga menerapkan 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 menerapkan penyiapan untuk bagian delegasi dengan membuat class helper yang menerapkan FishColor
. Anda membuat class dasar bernama GoldColor
yang mengimplementasikan FishColor
—yang dilakukannya hanyalah menyatakan bahwa warnanya adalah emas.
Membuat beberapa instance GoldColor
tidak masuk akal, karena semuanya akan melakukan hal yang sama persis. Jadi, Kotlin memungkinkan Anda mendeklarasikan class yang hanya dapat membuat satu instance dengan menggunakan kata kunci object
, bukan class
. Kotlin akan membuat satu instance tersebut, dan instance tersebut dirujuk oleh nama class. Kemudian, semua objek lain dapat menggunakan satu instance ini—tidak ada cara untuk membuat instance lain dari class ini. Jika Anda sudah terbiasa dengan pola singleton, begini cara menerapkan singleton di Kotlin.
- Di AquariumFish.kt, buat objek untuk
GoldColor
. Ganti warna.
object GoldColor : FishColor {
override val color = "gold"
}
Langkah 3: Tambahkan delegasi antarmuka untuk FishColor
Sekarang Anda siap menggunakan delegasi antarmuka.
- Di AquariumFish.kt, hapus penggantian
color
dariPlecostomus
. - Ubah class
Plecostomus
untuk mendapatkan warnanya dariGoldColor
. Anda melakukannya dengan menambahkanby GoldColor
ke deklarasi class, membuat delegasi. Artinya, alih-alih menerapkanFishColor
, gunakan penerapan yang disediakan olehGoldColor
. Jadi, setiap kalicolor
diakses, aksesnya didelegasikan keGoldColor
.
class Plecostomus: FishAction, FishColor by GoldColor {
override fun eat() {
println("eat algae")
}
}
Dengan kelas seperti ini, semua Pleco akan berwarna emas, tetapi ikan ini sebenarnya memiliki banyak warna. Anda dapat mengatasi hal ini dengan menambahkan parameter konstruktor untuk warna dengan GoldColor
sebagai warna default untuk Plecostomus
.
- Ubah class
Plecostomus
agar mengambilfishColor
yang diteruskan dengan konstruktornya, dan tetapkan 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
.
- Di 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. - Dalam deklarasi
Plecostomus
, delegasikanFishAction
kePrintingFishAction
, dengan meneruskan"eat algae"
. - Dengan semua delegasi tersebut, tidak ada kode di 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 menunjukkan class Shark
dan Plecostomus
, yang keduanya terdiri dari antarmuka PrintingFishAction
dan FishColor
, tetapi mendelegasikan implementasinya kepada mereka.
Delegasi antarmuka sangat berguna, dan Anda harus mempertimbangkan cara menggunakannya setiap kali Anda mungkin menggunakan class abstrak dalam bahasa lain. Dengan komposisi, Anda dapat memasukkan perilaku, bukan memerlukan banyak subkelas, yang masing-masing dikhususkan dengan cara yang berbeda.
Class data mirip dengan struct
di beberapa bahasa lain—class ini ada terutama untuk menyimpan beberapa data—tetapi objek class data tetap merupakan 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 diberikan Kotlin untuk class data.
Langkah 1: Buat class data
- Tambahkan paket
decor
baru 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 menjadikan
Decoration
class data, awali deklarasi class dengan kata kuncidata
. - Tambahkan properti
String
bernamarocks
untuk memberikan beberapa data ke class.
data class Decoration(val rocks: String) {
}
- Di 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()
, lalu jalankan program Anda. Perhatikan output yang masuk akal yang dibuat karena ini adalah class data.
⇒ Decoration(rocks=granite)
- Di
makeDecorations()
, buat dua objekDecoration
lagi yang keduanya "slate" dan cetak.
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 yang kedua 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
Tindakan ini disebut destrukturisasi dan merupakan singkatan yang berguna. Jumlah variabel harus cocok dengan jumlah properti, dan variabel ditetapkan dalam urutan yang dideklarasikan dalam class. Berikut 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 dengan menggunakan _
, bukan nama variabel, seperti yang ditunjukkan dalam kode di bawah.
val (rock, _, diver) = d5
Dalam tugas ini, Anda akan mempelajari beberapa class khusus di Kotlin, termasuk:
- Class singleton
- Enum
- Kelas tertutup
Langkah 1: Panggil 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 enum, yang memungkinkan Anda menghitung sesuatu dan merujuknya berdasarkan nama, seperti dalam bahasa lain. Deklarasikan enum dengan mengawali deklarasi dengan kata kunci enum
. Deklarasi enum dasar hanya memerlukan daftar nama, tetapi Anda juga dapat menentukan satu atau beberapa kolom yang terkait dengan setiap nama.
- Di Decoration.kt, coba contoh enum.
enum class Color(val rgb: Int) {
RED(0xFF0000), GREEN(0x00FF00), BLUE(0x0000FF);
}
Enum sedikit mirip singleton—hanya boleh ada satu, dan hanya boleh ada 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 merepresentasikan komponen warna. Anda juga bisa mendapatkan nilai ordinal enum menggunakan properti ordinal
, dan namanya menggunakan properti name
.
- Coba contoh enum lain.
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
Sealed class adalah class yang dapat dibuat subclass-nya, tetapi hanya di dalam file tempat class tersebut dideklarasikan. Jika Anda mencoba membuat subclass class dalam file yang berbeda, Anda akan mendapatkan error.
Karena class dan subclass berada dalam file yang sama, Kotlin akan mengetahui semua subclass secara statis. Artinya, pada waktu kompilasi, compiler melihat semua class dan subclass serta mengetahui bahwa ini adalah semuanya, sehingga compiler dapat melakukan pemeriksaan tambahan untuk Anda.
- Di AquariumFish.kt, coba contoh class tertutup, dengan tetap menggunakan 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 dibuat subclass di file lain. Jika ingin menambahkan lebih banyak jenis Seal
, Anda harus menambahkannya dalam file yang sama. Hal ini menjadikan class tertutup sebagai cara yang aman untuk merepresentasikan sejumlah tetap jenis. Misalnya, class tertutup sangat cocok untuk menampilkan keberhasilan atau error dari API jaringan.
Pelajaran ini mencakup banyak hal. Meskipun sebagian besar akan terasa familiar 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.
- Tentukan 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 di satu atau beberapa blok
init
. - Class dapat menentukan satu atau beberapa konstruktor sekunder menggunakan
constructor
, tetapi gaya Kotlin adalah menggunakan fungsi factory.
Pengubah visibilitas dan subclass
- Semua class dan fungsi di Kotlin bersifat
public
secara default, tetapi Anda dapat menggunakan pengubah untuk mengubah visibilitas menjadiinternal
,private
, atauprotected
. - Untuk membuat subclass, class induk harus ditandai
open
. - Untuk mengganti metode dan properti di subclass, metode dan properti harus ditandai
open
di class induk. - Class tertutup hanya dapat dibuat subclass dalam file yang sama tempat class tersebut ditentukan. Buat class tertutup dengan mengawali deklarasi dengan
sealed
.
Class data, singleton, dan enum
- Buat class data dengan mengawali deklarasi dengan
data
. - Destructuring adalah singkatan untuk menetapkan properti objek
data
ke variabel terpisah. - Buat class singleton dengan menggunakan
object
, bukanclass
. - Tentukan enum menggunakan
enum class
.
Class abstrak, antarmuka, dan delegasi
- Class dan antarmuka abstrak adalah dua cara untuk berbagi perilaku umum antar-class.
- Class abstrak menentukan properti dan perilaku, tetapi menyerahkan implementasi ke subclass.
- Antarmuka menentukan perilaku, dan dapat memberikan implementasi default untuk beberapa atau semua perilaku.
- Saat Anda menggunakan antarmuka untuk menyusun class, fungsi class diperluas melalui instance class yang dikandungnya.
- Delegasi antarmuka menggunakan komposisi, tetapi juga mendelegasikan penerapan ke class antarmuka.
- Komposisi adalah cara efektif untuk menambahkan fungsi ke class menggunakan delegasi antarmuka. Secara umum, komposisi lebih disukai, tetapi pewarisan dari class abstrak lebih cocok untuk beberapa masalah.
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.
- Class dan pewarisan
- Konstruktor
- Fungsi pabrik
- Properti dan kolom
- Pengubah visibilitas
- Class abstrak
- Antarmuka
- Delegasi
- Class data
- Persamaan
- Destrukturisasi
- Deklarasi objek
- Class enum
- Class terkunci
- Menangani Error Opsional Menggunakan Class Tertutup Kotlin
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
Class memiliki metode khusus yang berfungsi sebagai cetak biru untuk membuat objek class tersebut. Apa nama metodenya?
▢ Builder
▢ Instantiator
▢ Konstruktor
▢ Cetak biru
Pertanyaan 2
Manakah dari pernyataan berikut tentang antarmuka dan class abstrak yang TIDAK benar?
▢ Class abstrak dapat memiliki konstruktor.
▢ Antarmuka tidak dapat memiliki konstruktor.
▢ Antarmuka dan class abstrak dapat di-instantiate secara langsung.
▢ Properti abstrak harus diterapkan 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
Pertimbangkan class data ini:data class Fish(val name: String, val species:String, val colors:String)
Manakah dari kode berikut yang BUKAN kode valid untuk membuat dan mendekonstruksi 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
Misalkan Anda memiliki kebun binatang dengan banyak hewan yang perlu dirawat. Manakah dari pilihan berikut yang BUKAN merupakan bagian dari penerapan pengawasan?
▢ interface
untuk berbagai jenis makanan yang dimakan hewan.
▢ Class abstract Caretaker
yang dapat Anda gunakan untuk membuat berbagai jenis pengasuh.
▢ interface
untuk memberikan air bersih kepada 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: Welcome to the course".