1. Sebelum memulai
Codelab ini mengajarkan cara menggunakan class dan objek di Kotlin.
Class menyediakan cetak biru tempat objek dapat dibuat. Objek adalah instance dari class yang terdiri dari data yang spesifik untuk objek tersebut. Anda dapat menggunakan objek atau instance class secara bergantian.
Sebagai analogi, bayangkan Anda akan membangun rumah. Class serupa dengan rencana desain arsitek, juga dikenal sebagai cetak biru. Cetak biru bukanlah rumah, melainkan petunjuk cara membangun rumah. Rumah adalah hal yang sebenarnya, atau objek, yang dibuat berdasarkan cetak biru.
Sama seperti cetak biru rumah yang mendeskripsikan beberapa ruangan dan setiap ruangan memiliki desain dan tujuan masing-masing, setiap class memiliki desain dan tujuannya sendiri. Untuk mengetahui cara mendesain class, Anda harus memahami pemrograman berorientasi objek (OOP), sebuah framework yang mengajarkan Anda untuk memasukkan data, logika, dan perilaku dalam objek.
OOP membantu Anda menyederhanakan masalah dunia nyata yang kompleks menjadi objek yang lebih kecil. Ada empat konsep dasar OOP yang masing-masing akan Anda pelajari lebih lanjut di codelab ini:
- Enkapsulasi. Menggabungkan properti dan metode terkait yang melakukan tindakan pada properti tersebut dalam class. Misalnya, perhatikan ponsel Anda. Ponsel itu mencakup kamera, layar, kartu memori, dan beberapa komponen hardware dan software lainnya. Anda tidak perlu khawatir tentang cara komponen disambungkan secara internal.
- Abstraksi. Ekstensi untuk enkapsulasi. Idenya adalah sebisa mungkin menyembunyikan logika implementasi internal. Misalnya, untuk mengambil foto dengan ponsel, yang perlu Anda lakukan adalah membuka aplikasi kamera, mengarahkan ponsel ke adegan yang ingin Anda ambil, dan mengklik tombol untuk mengambil foto. Anda tidak perlu mengetahui cara aplikasi kamera dibuat atau cara kerja hardware kamera yang sebenarnya. Singkatnya, mekanisme internal pada aplikasi kamera dan cara kamera seluler mengambil foto diabstraksi untuk memungkinkan Anda melakukan tugas yang penting.
- Pewarisan. Memungkinkan Anda membangun class berdasarkan karakteristik dan perilaku class lain dengan menetapkan hubungan induk-turunan. Misalnya, ada berbagai produsen yang memproduksi berbagai perangkat seluler yang menjalankan Android OS, tetapi UI untuk setiap perangkat berbeda. Dengan kata lain, produsen mewarisi fitur Android OS dan membuat penyesuaian di atasnya.
- Polimorfisme. Kata tersebut adalah adaptasi dari akar kata Yunani poly- yang berarti banyak, dan -morphism yang berarti bentuk. Polimorfisme adalah kemampuan untuk menggunakan berbagai objek dengan satu cara yang sama. Misalnya, saat Anda menghubungkan speaker Bluetooth ke ponsel, ponsel itu hanya perlu mengetahui bahwa ada perangkat yang dapat memutar audio melalui Bluetooth. Namun, ada berbagai speaker Bluetooth yang dapat Anda pilih, dan ponsel Anda tidak perlu mengetahui cara bekerja dengan setiap speaker tersebut secara khusus.
Terakhir, Anda akan mempelajari delegasi properti yang memberikan kode yang dapat digunakan kembali untuk mengelola nilai properti dengan sintaksis yang ringkas. Dalam codelab ini, Anda akan mempelajari konsep tersebut saat membangun struktur class untuk aplikasi smart-home.
Prasyarat
- Cara membuka, mengedit, dan menjalankan kode di Kotlin Playground.
- Pengetahuan tentang dasar-dasar pemrograman Kotlin, termasuk variabel, fungsi, serta fungsi
println()
danmain()
Yang akan Anda pelajari
- Ringkasan tentang OOP.
- Pengertian class.
- Cara menentukan class dengan konstruktor, fungsi, dan properti.
- Cara membuat instance objek.
- Pengertian pewarisan.
- Perbedaan antara hubungan IS-A dan HAS-A.
- Cara mengganti properti dan fungsi.
- Pengertian pengubah visibilitas.
- Pengertian delegasi dan cara menggunakan delegasi
by
.
Yang akan Anda bangun
- Struktur class smart-home.
- Class yang merepresentasikan perangkat smart, seperti smart TV dan lampu smart.
Yang akan Anda butuhkan
- Komputer dengan akses internet dan browser web
2. Menentukan class
Saat menentukan class, Anda menentukan properti dan metode yang harus dimiliki semua objek class tersebut.
Definisi class dimulai dengan kata kunci class
, diikuti dengan nama dan sepasang tanda kurung kurawal. Bagian dari sintaksis sebelum kurung kurawal buka juga disebut sebagai header class. Dalam tanda kurung kurawal, Anda dapat menentukan properti dan fungsi untuk class. Anda akan segera mempelajari properti dan fungsi. Anda dapat melihat sintaksis definisi class dalam diagram ini:
Berikut adalah konvensi penamaan yang direkomendasikan untuk class:
- Anda dapat memilih nama class apa pun yang Anda inginkan, tetapi jangan gunakan kata kunci Kotlin sebagai nama class, seperti kata kunci
fun
. - Nama class ditulis dalam PascalCase sehingga setiap kata dimulai dengan huruf kapital dan tidak ada spasi di antara kata tersebut. Misalnya, pada kata PerangkatSmart, huruf pertama setiap kata ditulis dengan huruf kapital dan tidak ada spasi di antara kata tersebut.
Class terdiri dari tiga bagian utama:
- Properti. Variabel yang menentukan atribut objek class.
- Metode. Fungsi yang berisi tindakan dan perilaku class.
- Konstruktor. Fungsi anggota khusus yang membuat instance class di seluruh program yang menentukannya.
Ini bukan pertama kalinya Anda bekerja dengan class. Pada codelab sebelumnya, Anda telah mempelajari jenis data, seperti jenis data Int
, Float
, String
, dan Double
. Jenis data ini ditentukan sebagai class di Kotlin. Saat Anda menentukan variabel seperti yang ditunjukkan dalam cuplikan kode ini, Anda akan membuat objek dari class Int
, yang instance-nya dibuat dengan nilai 1
:
val number: Int = 1
Tentukan class SmartDevice
:
- Di Kotlin Playground, ganti konten dengan fungsi
main()
kosong:
fun main() {
}
- Pada baris sebelum fungsi
main()
, tentukan classSmartDevice
dengan isi yang menyertakan komentar//
empty
body
:
class SmartDevice {
// empty body
}
fun main() {
}
3. Membuat instance class
Seperti yang telah Anda pelajari, class adalah cetak biru untuk objek. Runtime Kotlin menggunakan class, atau cetak biru, untuk membuat objek dari jenis tertentu. Dengan class SmartDevice
, Anda memiliki cetak biru tentang pengertian perangkat smart. Untuk memiliki perangkat smart sebenarnya dalam program, Anda harus membuat instance objek SmartDevice
. Sintaksis pembuatan instance dimulai dengan nama class, diikuti sepasang tanda kurung seperti yang dapat Anda lihat dalam diagram ini:
Untuk menggunakan objek, Anda membuat objek dan menetapkannya ke variabel, mirip dengan cara Anda menentukan variabel. Anda menggunakan kata kunci val
untuk membuat variabel yang tidak dapat diubah dan kata kunci var
untuk variabel yang dapat diubah. Kata kunci val
atau var
diikuti dengan nama variabel, lalu operator penetapan =
, kemudian pembuatan instance objek class. Anda dapat melihat sintaksisnya dalam diagram ini:
Buat instance class SmartDevice
sebagai objek:
- Dalam fungsi
main()
, gunakan kata kuncival
untuk membuat variabel bernamasmartTvDevice
dan melakukan inisialisasi sebagai instance dari classSmartDevice
:
fun main() {
val smartTvDevice = SmartDevice()
}
4. Menentukan metode class
Di Unit 1, Anda telah mempelajari bahwa:
- Definisi fungsi menggunakan kata kunci
fun
, diikuti sepasang tanda kurung dan sepasang tanda kurung kurawal. Kurung kurawal berisi kode yang merupakan petunjuk yang diperlukan untuk mengeksekusi tugas. - Pemanggilan fungsi menyebabkan kode yang dimuat dalam fungsi tersebut dieksekusi.
Tindakan yang dapat dilakukan class ditentukan sebagai fungsi di class tersebut. Misalnya, bayangkan Anda memiliki perangkat smart, smart TV, atau lampu smart, yang dapat diaktifkan dan dinonaktifkan dengan ponsel. Perangkat smart diterjemahkan ke class SmartDevice
dalam pemrograman, dan tindakan untuk mengaktifkan dan menonaktifkannya direpresentasikan oleh fungsi turnOn()
dan turnOff()
yang mengaktifkan perilaku aktif dan nonaktif.
Sintaksis untuk menentukan fungsi di class sama dengan yang telah Anda pelajari sebelumnya. Satu-satunya perbedaan adalah fungsi tersebut ditempatkan dalam isi class. Saat Anda menentukan fungsi dalam isi class, fungsi itu disebut sebagai fungsi anggota atau metode, dan merepresentasikan perilaku class. Untuk codelab ini, fungsi disebut sebagai metode setiap kali fungsi tersebut muncul dalam isi class.
Tentukan metode turnOn()
dan turnOff()
di class SmartDevice
:
- Dalam isi class
SmartDevice
, tentukan metodeturnOn()
dengan isi kosong:
class SmartDevice {
fun turnOn() {
}
}
- Di bagian isi metode
turnOn()
, tambahkan pernyataanprintln()
, lalu teruskan string"Smart
device
is
turned
on."
:
class SmartDevice {
fun turnOn() {
println("Smart device is turned on.")
}
}
- Setelah metode
turnOn()
, tambahkan metodeturnOff()
yang mencetak string"Smart
device
is
turned
off."
:
class SmartDevice {
fun turnOn() {
println("Smart device is turned on.")
}
fun turnOff() {
println("Smart device is turned off.")
}
}
Memanggil metode pada objek
Sejauh ini, Anda sudah menentukan class yang berfungsi sebagai cetak biru untuk perangkat smart, membuat instance class, dan menetapkan instance ke variabel. Sekarang Anda menggunakan metode class SmartDevice
untuk mengaktifkan dan menonaktifkan perangkat.
Panggilan ke metode di class serupa dengan cara Anda memanggil fungsi lain dari fungsi main()
dalam codelab sebelumnya. Misalnya, jika Anda perlu memanggil metode turnOff()
dari metode turnOn()
, Anda dapat menulis sesuatu yang mirip dengan cuplikan kode ini:
class SmartDevice {
fun turnOn() {
// A valid use case to call the turnOff() method could be to turn off the TV when available power doesn't meet the requirement.
turnOff()
...
}
...
}
Untuk memanggil metode class di luar class, mulai dengan objek class, diikuti dengan operator .
, nama fungsi, dan sepasang tanda kurung. Jika diperlukan, tanda kurung dapat berisi argumen yang dibutuhkan oleh metode. Anda dapat melihat sintaksisnya dalam diagram ini:
Panggil metode turnOn()
dan turnOff()
pada objek:
- Pada fungsi
main()
di baris setelah variabelsmartTvDevice
, panggil metodeturnOn()
:
fun main() {
val smartTvDevice = SmartDevice()
smartTvDevice.turnOn()
}
- Pada baris setelah metode
turnOn()
, panggil metodeturnOff()
:
fun main() {
val smartTvDevice = SmartDevice()
smartTvDevice.turnOn()
smartTvDevice.turnOff()
}
- Jalankan kode.
Outputnya adalah sebagai berikut:
Smart device is turned on. Smart device is turned off.
5. Menentukan properti class
Di Unit 1, Anda telah mempelajari variabel, yang merupakan container untuk data-data tunggal. Anda telah mempelajari cara membuat variabel baca-saja dengan kata kunci val
dan variabel yang dapat diubah dengan kata kunci var
.
Meskipun metode menentukan tindakan yang dapat dilakukan class, properti menentukan atribut data atau karakteristik class. Misalnya, perangkat smart memiliki properti berikut:
- Nama. Nama perangkat.
- Kategori. Jenis perangkat smart, seperti hiburan, aplikasi utilitas, atau memasak.
- Status perangkat. Apakah perangkat aktif, nonaktif, online, atau offline. Perangkat dianggap online jika terhubung ke internet. Jika tidak, perangkat akan dianggap offline.
Pada dasarnya, properti adalah variabel yang ditentukan dalam isi class, bukan isi fungsi. Ini berarti sintaksis untuk menentukan properti dan variabel identik. Anda menentukan properti yang tidak dapat diubah dengan kata kunci val
dan properti yang dapat diubah dengan kata kunci var
.
Terapkan karakteristik yang disebutkan di atas sebagai properti class SmartDevice
:
- Pada baris sebelum metode
turnOn()
, tentukan propertiname
dan tetapkan properti tersebut ke string"Android
TV"
:
class SmartDevice {
val name = "Android TV"
fun turnOn() {
println("Smart device is turned on.")
}
fun turnOff() {
println("Smart device is turned off.")
}
}
- Pada baris setelah properti
name
, tentukan properticategory
dan tetapkan ke string"Entertainment"
, lalu tentukan propertideviceStatus
dan tetapkan ke string"online"
:
class SmartDevice {
val name = "Android TV"
val category = "Entertainment"
var deviceStatus = "online"
fun turnOn() {
println("Smart device is turned on.")
}
fun turnOff() {
println("Smart device is turned off.")
}
}
- Pada baris setelah variabel
smartTvDevice
, panggil fungsiprintln()
, lalu teruskan string"Device
name
is:
${smartTvDevice.name}"
:
fun main() {
val smartTvDevice = SmartDevice()
println("Device name is: ${smartTvDevice.name}")
smartTvDevice.turnOn()
smartTvDevice.turnOff()
}
- Jalankan kode.
Outputnya adalah sebagai berikut:
Device name is: Android TV Smart device is turned on. Smart device is turned off.
Fungsi pengambil dan penyetel dalam properti
Properti dapat melakukan lebih dari yang dilakukan variabel. Misalnya, bayangkan Anda membuat struktur class untuk merepresentasikan smart TV. Salah satu tindakan umum yang Anda lakukan adalah meningkatkan dan menurunkan volume. Untuk merepresentasikan tindakan ini dalam pemrograman, Anda dapat membuat properti bernama speakerVolume
yang menyimpan tingkat volume saat ini yang disetel di speaker TV, tetapi ada rentang tempat nilai volume disimpan. Volume minimum yang dapat disetel adalah 0, sedangkan volume maksimum adalah 100. Untuk memastikan properti speakerVolume
tidak pernah melampaui 100 atau turun di bawah 0, Anda dapat menulis fungsi penyetel. Saat mengupdate nilai properti, Anda harus memeriksa apakah nilai berada dalam rentang 0 hingga 100. Contoh lainnya, bayangkan ada persyaratan untuk memastikan bahwa nama selalu dalam huruf kapital. Anda dapat menerapkan fungsi pengambil untuk mengonversi properti name
menjadi huruf kapital.
Sebelum mempelajari lebih lanjut cara menerapkan properti ini, Anda perlu memahami sintaksis lengkap untuk mendeklarasikannya. Sintaksis lengkap untuk menentukan properti yang dapat diubah dimulai dengan definisi variabel, diikuti dengan fungsi get()
dan set()
opsional. Anda dapat melihat sintaksisnya dalam diagram ini:
Jika Anda tidak menentukan fungsi pengambil dan penyetel untuk properti, compiler Kotlin akan membuat fungsi secara internal. Misalnya, jika Anda menggunakan kata kunci var
untuk menentukan properti speakerVolume
dan memberinya nilai 2
, compiler akan otomatis membuat fungsi pengambil dan penyetel seperti yang dapat Anda lihat di cuplikan kode ini:
var speakerVolume = 2
get() = field
set(value) {
field = value
}
Anda tidak akan melihat baris ini dalam kode Anda karena baris tersebut ditambahkan oleh compiler di latar belakang.
Sintaksis lengkap untuk properti yang tidak dapat diubah memiliki dua perbedaan:
- Dimulai dengan kata kunci
val
. - Variabel jenis
val
adalah variabel baca-saja sehingga tidak memiliki fungsiset()
.
Properti Kotlin menggunakan kolom pendukung untuk menyimpan nilai di memori. Kolom pendukung pada dasarnya adalah variabel class yang ditentukan secara internal di properti. Kolom pendukung dibatasi untuk properti, yang berarti Anda hanya dapat mengaksesnya melalui fungsi properti get()
atau set()
.
Untuk membaca nilai properti dalam fungsi get()
atau mengupdate nilai di fungsi set()
, Anda harus menggunakan kolom pendukung properti. Kolom pendukung ini otomatis dibuat oleh compiler Kotlin dan direferensikan dengan ID field
.
Misalnya, saat Anda ingin mengupdate nilai properti dalam fungsi set()
, gunakan parameter fungsi set()
yang disebut sebagai parameter value
, dan tetapkan ke variabel field
seperti yang dapat Anda lihat di cuplikan kode ini:
var speakerVolume = 2
set(value) {
field = value
}
Misalnya, untuk memastikan nilai yang ditetapkan ke properti speakerVolume
berada dalam rentang 0 hingga 100, Anda dapat menerapkan fungsi penyetel seperti yang dapat Anda lihat di cuplikan kode ini:
var speakerVolume = 2
set(value) {
if (value in 0..100) {
field = value
}
}
Fungsi set()
memeriksa apakah nilai Int
berada dalam rentang 0 hingga 100 dengan menggunakan kata kunci in
, diikuti dengan rentang nilai. Jika nilai berada dalam rentang yang diharapkan, nilai field
akan diupdate. Jika tidak, nilai properti tidak berubah.
Anda akan menyertakan properti ini di class di bagian Mengimplementasikan hubungan antar-class dalam codelab ini, sehingga Anda tidak perlu menambahkan fungsi penyetel ke kode sekarang.
6. Menentukan konstruktor
Tujuan utama konstruktor adalah menentukan cara pembuatan objek class. Dengan kata lain, konstruktor melakukan inisialisasi objek dan menyiapkan objek tersebut untuk digunakan. Anda melakukannya saat membuat instance objek. Kode di dalam konstruktor dieksekusi ketika objek class dibuat instance-nya. Anda dapat menentukan konstruktor dengan atau tanpa parameter.
Konstruktor default
Konstruktor default adalah konstruktor tanpa parameter. Anda dapat menentukan konstruktor default seperti yang ditunjukkan dalam cuplikan kode ini:
class SmartDevice constructor() {
...
}
Kotlin bertujuan agar ringkas sehingga Anda dapat menghapus kata kunci constructor
jika tidak ada anotasi atau pengubah visibilitas, yang akan segera Anda pelajari di konstruktor. Anda juga dapat menghapus tanda kurung jika konstruktor tidak memiliki parameter seperti yang ditunjukkan dalam cuplikan kode ini:
class SmartDevice {
...
}
Compiler Kotlin secara otomatis membuat konstruktor default. Anda tidak akan melihat konstruktor default yang dibuat secara otomatis dalam kode Anda karena ditambahkan oleh compiler di latar belakang.
Menentukan konstruktor berparameter
Di class SmartDevice
, properti name
dan category
tidak dapat diubah. Anda harus memastikan semua instance class SmartDevice
melakukan inisialisasi pada properti name
dan category
. Dengan implementasi saat ini, nilai untuk properti name
dan category
di-hardcode. Ini berarti bahwa semua perangkat smart diberi nama dengan string "Android
TV"
dan dikategorikan dengan string "Entertainment"
.
Untuk mempertahankan sifatnya yang tidak dapat diubah tetapi menghindari nilai hardcode, gunakan konstruktor berparameter untuk melakukan inisialisasi terhadapnya:
- Di class
SmartDevice
, pindahkan propertiname
dancategory
ke konstruktor tanpa menetapkan nilai default:
class SmartDevice(val name: String, val category: String) {
var deviceStatus = "online"
fun turnOn() {
println("Smart device is turned on.")
}
fun turnOff() {
println("Smart device is turned off.")
}
}
Konstruktor sekarang menerima parameter untuk menyiapkan propertinya, sehingga cara membuat instance objek untuk class tersebut juga berubah. Anda dapat melihat sintaksis lengkap untuk membuat instance objek dalam diagram ini:
Berikut ini representasi kode:
SmartDevice("Android TV", "Entertainment")
Kedua argumen untuk konstruktor adalah string. Kurang jelas parameter mana yang nilainya harus ditetapkan. Untuk mengatasinya, serupa dengan cara meneruskan argumen fungsi, Anda dapat membuat konstruktor dengan argumen bernama seperti yang ditunjukkan dalam cuplikan kode ini:
SmartDevice(name = "Android TV", category = "Entertainment")
Ada dua jenis utama konstruktor di Kotlin:
- Konstruktor utama. Class hanya dapat memiliki satu konstruktor utama, yang didefinisikan sebagai bagian dari header class. Konstruktor utama dapat berupa konstruktor default atau berparameter. Konstruktor utama tidak memiliki isi. Artinya, konstruktor tersebut tidak boleh berisi kode apa pun.
- Konstruktor sekunder. Class dapat memiliki beberapa konstruktor sekunder. Anda dapat menentukan konstruktor sekunder dengan atau tanpa parameter. Konstruktor sekunder dapat melakukan inisialisasi pada class dan memiliki isi yang dapat berisi logika inisialisasi. Jika class memiliki konstruktor utama, setiap konstruktor sekunder perlu melakukan inisialisasi pada konstruktor utama.
Anda dapat menggunakan konstruktor utama untuk melakukan inisialisasi pada properti di header class. Argumen yang diteruskan ke konstruktor ditetapkan ke properti. Sintaksis untuk menentukan konstruktor utama dimulai dengan nama class, diikuti dengan kata kunci constructor
dan sepasang tanda kurung. Tanda kurung berisi parameter untuk konstruktor utama. Jika ada lebih dari satu parameter, pisahkan definisi parameter dengan koma. Anda dapat melihat sintaksis lengkap untuk menentukan konstruktor utama dalam diagram ini:
Konstruktor sekunder dimasukkan dalam isi class, dan sintaksisnya mencakup tiga bagian:
- Deklarasi konstruktor sekunder. Definisi konstruktor sekunder dimulai dengan kata kunci
constructor
, diikuti dengan tanda kurung. Jika diperlukan, tanda kurung dapat berisi parameter yang dibutuhkan oleh konstruktor sekunder. - Inisialisasi konstruktor utama. Inisialisasi dimulai dengan titik dua, diikuti kata kunci
this
dan sepasang tanda kurung. Jika diperlukan, tanda kurung dapat berisi parameter yang dibutuhkan oleh konstruktor utama. - Isi konstruktor sekunder. Inisialisasi konstruktor utama diikuti oleh sepasang tanda kurung kurawal yang memuat isi konstruktor sekunder.
Anda dapat melihat sintaksisnya dalam diagram ini:
Misalnya, Anda ingin mengintegrasikan API yang dikembangkan oleh penyedia perangkat smart. Namun, API tersebut menampilkan kode status jenis Int
untuk menunjukkan status perangkat awal. API menampilkan nilai 0
jika perangkat offline dan nilai 1
jika perangkat online. Untuk nilai integer lainnya, status dianggap tidak diketahui. Anda dapat membuat konstruktor sekunder di class SmartDevice
untuk mengonversi parameter statusCode
ini menjadi representasi string seperti yang dapat Anda lihat di cuplikan kode ini:
class SmartDevice(val name: String, val category: String) {
var deviceStatus = "online"
constructor(name: String, category: String, statusCode: Int) : this(name, category) {
deviceStatus = when (statusCode) {
0 -> "offline"
1 -> "online"
else -> "unknown"
}
}
...
}
7. Mengimplementasikan hubungan antar-class
Pewarisan memungkinkan Anda membuat class berdasarkan karakteristik dan perilaku class lain. Ini adalah mekanisme canggih yang membantu Anda menulis kode yang dapat digunakan kembali dan membangun hubungan antar-class.
Misalnya, ada banyak perangkat smart di pasar, seperti smart TV, lampu smart, dan tombol smart. Saat Anda merepresentasikan perangkat smart dalam pemrograman, perangkat tersebut memiliki beberapa properti yang sama, seperti nama, kategori, dan status. Perangkat tersebut juga memiliki perilaku yang sama, seperti kemampuan untuk mengaktifkan dan menonaktifkannya.
Namun, cara untuk mengaktifkan atau menonaktifkan setiap perangkat smart berbeda. Misalnya, untuk mengaktifkan TV, Anda mungkin perlu mengaktifkan layar, lalu menyiapkan tingkat volume dan saluran terakhir yang diketahui. Di sisi lain, untuk menyalakan lampu, Anda mungkin hanya perlu meningkatkan atau menurunkan kecerahan.
Selain itu, setiap perangkat smart memiliki lebih banyak fungsi dan tindakan yang dapat dilakukan. Misalnya, dengan TV, Anda dapat menyesuaikan volume dan mengubah saluran. Dengan lampu, Anda dapat menyesuaikan kecerahan atau warna.
Singkatnya, semua perangkat smart memiliki fitur yang berbeda-beda, tetapi memiliki beberapa karakteristik yang sama. Anda dapat menduplikasi karakteristik yang sama ini ke setiap class perangkat smart atau membuat kode dapat digunakan kembali dengan pewarisan.
Untuk melakukannya, Anda harus membuat class induk SmartDevice
, serta menentukan properti dan perilaku yang sama. Kemudian, Anda dapat membuat class turunan, seperti class SmartTvDevice
dan SmartLightDevice
, yang mewarisi properti class induk.
Dalam istilah pemrograman, kita mengatakan bahwa class SmartTvDevice
dan SmartLightDevice
memperluas class induk SmartDevice
. Class induk juga disebut sebagai superclass dan class turunan sebagai subclass. Anda dapat melihat hubungan antara keduanya dalam diagram ini:
Namun, dalam Kotlin, semua class bersifat final secara default, yang berarti Anda tidak dapat memperluasnya, jadi Anda harus menentukan hubungan antar-class.
Tentukan hubungan antara superclass SmartDevice
dan subclass-nya:
- Di superclass
SmartDevice
, tambahkan kata kunciopen
sebelum kata kunciclass
agar dapat diperluas:
open class SmartDevice(val name: String, val category: String) {
...
}
Kata kunci open
memberi tahu compiler bahwa class ini dapat diperluas, sehingga sekarang class lain dapat memperluasnya.
Sintaksis untuk membuat subclass dimulai dengan pembuatan header class seperti yang telah Anda lakukan sejauh ini. Kurung tutup konstruktor diikuti dengan spasi, titik dua, spasi lain, nama superclass, dan sepasang tanda kurung. Jika diperlukan, tanda kurung dapat menyertakan parameter yang dibutuhkan oleh konstruktor superclass. Anda dapat melihat sintaksisnya dalam diagram ini:
- Buat subclass
SmartTvDevice
yang memperluas superclassSmartDevice
:
class SmartTvDevice(deviceName: String, deviceCategory: String) :
SmartDevice(name = deviceName, category = deviceCategory) {
}
Definisi constructor
untuk SmartTvDevice
tidak menentukan apakah properti dapat diubah atau tidak dapat diubah. Ini berarti parameter deviceName
dan deviceCategory
hanyalah parameter constructor
, bukan properti class. Anda tidak akan dapat menggunakannya di class, tetapi cukup teruskan ke konstruktor superclass.
- Di bagian isi subclass
SmartTvDevice
, tambahkan propertispeakerVolume
yang Anda buat saat mempelajari fungsi pengambil dan penyetel:
class SmartTvDevice(deviceName: String, deviceCategory: String) :
SmartDevice(name = deviceName, category = deviceCategory) {
var speakerVolume = 2
set(value) {
if (value in 0..100) {
field = value
}
}
}
- Tentukan properti
channelNumber
yang ditetapkan ke nilai1
dengan fungsi penyetel yang menentukan rentang0..200
:
class SmartTvDevice(deviceName: String, deviceCategory: String) :
SmartDevice(name = deviceName, category = deviceCategory) {
var speakerVolume = 2
set(value) {
if (value in 0..100) {
field = value
}
}
var channelNumber = 1
set(value) {
if (value in 0..200) {
field = value
}
}
}
- Tentukan metode
increaseSpeakerVolume()
yang meningkatkan volume dan mencetak string"Speaker
volume
increased
to
$speakerVolume."
:
class SmartTvDevice(deviceName: String, deviceCategory: String) :
SmartDevice(name = deviceName, category = deviceCategory) {
var speakerVolume = 2
set(value) {
if (value in 0..100) {
field = value
}
}
var channelNumber = 1
set(value) {
if (value in 0..200) {
field = value
}
}
fun increaseSpeakerVolume() {
speakerVolume++
println("Speaker volume increased to $speakerVolume.")
}
}
- Tambahkan metode
nextChannel()
yang meningkatkan jumlah saluran dan mencetak string"Channel
number
increased
to
$channelNumber."
:
class SmartTvDevice(deviceName: String, deviceCategory: String) :
SmartDevice(name = deviceName, category = deviceCategory) {
var speakerVolume = 2
set(value) {
if (value in 0..100) {
field = value
}
}
var channelNumber = 1
set(value) {
if (value in 0..200) {
field = value
}
}
fun increaseSpeakerVolume() {
speakerVolume++
println("Speaker volume increased to $speakerVolume.")
}
fun nextChannel() {
channelNumber++
println("Channel number increased to $channelNumber.")
}
}
- Pada baris setelah subclass
SmartTvDevice
, tentukan subclassSmartLightDevice
yang memperluas superclassSmartDevice
:
class SmartLightDevice(deviceName: String, deviceCategory: String) :
SmartDevice(name = deviceName, category = deviceCategory) {
}
- Dalam isi subclass
SmartLightDevice
, tentukan propertibrightnessLevel
yang ditetapkan ke nilai0
dengan fungsi penyetel yang menentukan rentang0..100
:
class SmartLightDevice(deviceName: String, deviceCategory: String) :
SmartDevice(name = deviceName, category = deviceCategory) {
var brightnessLevel = 0
set(value) {
if (value in 0..100) {
field = value
}
}
}
- Tentukan metode
increaseBrightness()
yang meningkatkan kecerahan lampu dan mencetak string"Brightness
increased
to
$brightnessLevel."
:
class SmartLightDevice(deviceName: String, deviceCategory: String) :
SmartDevice(name = deviceName, category = deviceCategory) {
var brightnessLevel = 0
set(value) {
if (value in 0..100) {
field = value
}
}
fun increaseBrightness() {
brightnessLevel++
println("Brightness increased to $brightnessLevel.")
}
}
Hubungan antar-class
Jika menggunakan pewarisan, Anda akan menetapkan hubungan antara dua class dalam sesuatu yang disebut hubungan IS-A. Objek juga merupakan instance class yang memberikan warisan. Dalam hubungan HAS-A, objek dapat memiliki instance class lain tanpa benar-benar menjadi instance class itu sendiri. Anda dapat melihat representasi tingkat tinggi dari hubungan ini dalam diagram ini:
Hubungan IS-A
Saat Anda menentukan hubungan IS-A antara superclass SmartDevice
dan subclass SmartTvDevice
, artinya, apa pun yang dapat dilakukan oleh superclass SmartDevice
dapat juga dilakukan oleh subclass SmartTvDevice
. Hubungannya bersifat satu arah, sehingga Anda dapat mengatakan bahwa setiap smart TV adalah perangkat smart, tetapi Anda tidak dapat mengatakan bahwa setiap perangkat smart adalah smart TV. Representasi kode untuk hubungan IS-A ditunjukkan dalam cuplikan kode ini:
// Smart TV IS-A smart device.
class SmartTvDevice : SmartDevice() {
}
Jangan gunakan pewarisan hanya untuk mencapai penggunaan kembali kode. Sebelum memutuskan, periksa apakah kedua class tersebut saling berhubungan. Jika keduanya menunjukkan adanya hubungan, periksa apakah keduanya benar-benar memenuhi syarat untuk hubungan IS-A. Tanyakan pada diri sendiri, "Bisakah saya katakan bahwa subclass adalah superclass?". Misalnya, Android adalah sistem operasi.
Hubungan HAS-A
Hubungan HAS-A adalah cara lain untuk menentukan hubungan antara dua kelas. Misalnya, Anda mungkin akan menggunakan smart TV di rumah Anda. Dalam hal ini, ada hubungan antara smart TV dan rumah. Rumah berisi perangkat smart atau, dengan kata lain, rumah memiliki perangkat smart. Hubungan HAS-A antara dua class juga disebut sebagai komposisi.
Sejauh ini, Anda telah membuat beberapa perangkat smart. Sekarang, Anda akan membuat class SmartHome
yang berisi perangkat smart. Class SmartHome
memungkinkan Anda berinteraksi dengan perangkat smart.
Gunakan hubungan HAS-A untuk menentukan class SmartHome
:
- Di antara class
SmartLightDevice
dan fungsimain()
, tentukan classSmartHome
:
class SmartLightDevice(deviceName: String, deviceCategory: String) :
SmartDevice(name = deviceName, category = deviceCategory) {
...
}
class SmartHome {
}
fun main() {
...
}
- Pada konstruktor class
SmartHome
, gunakan kata kuncival
untuk membuat propertismartTvDevice
dari jenisSmartTvDevice
:
// The SmartHome class HAS-A smart TV device.
class SmartHome(val smartTvDevice: SmartTvDevice) {
}
- Dalam isi class
SmartHome
, tentukan metodeturnOnTv()
yang memanggil metodeturnOn()
di propertismartTvDevice
:
class SmartHome(val smartTvDevice: SmartTvDevice) {
fun turnOnTv() {
smartTvDevice.turnOn()
}
}
- Pada baris setelah metode
turnOnTv()
, tentukan metodeturnOffTv()
yang memanggil metodeturnOff()
di propertismartTvDevice
:
class SmartHome(val smartTvDevice: SmartTvDevice) {
fun turnOnTv() {
smartTvDevice.turnOn()
}
fun turnOffTv() {
smartTvDevice.turnOff()
}
}
- Pada baris setelah metode
turnOffTv()
, tentukan metodeincreaseTvVolume()
yang memanggil metodeincreaseSpeakerVolume()
di propertismartTvDevice
, lalu tentukan metodechangeTvChannelToNext()
yang memanggil metodenextChannel()
di propertismartTvDevice
:
class SmartHome(val smartTvDevice: SmartTvDevice) {
fun turnOnTv() {
smartTvDevice.turnOn()
}
fun turnOffTv() {
smartTvDevice.turnOff()
}
fun increaseTvVolume() {
smartTvDevice.increaseSpeakerVolume()
}
fun changeTvChannelToNext() {
smartTvDevice.nextChannel()
}
}
- Pada konstruktor class
SmartHome
, pindahkan parameter propertismartTvDevice
ke barisnya sendiri, diikuti dengan koma:
class SmartHome(
val smartTvDevice: SmartTvDevice,
) {
...
}
- Pada baris setelah properti
smartTvDevice
, gunakan kata kuncival
untuk menentukan propertismartLightDevice
dari jenisSmartLightDevice
:
// The SmartHome class HAS-A smart TV device and smart light.
class SmartHome(
val smartTvDevice: SmartTvDevice,
val smartLightDevice: SmartLightDevice
) {
...
}
- Di bagian isi
SmartHome
, tentukan metodeturnOnLight()
yang memanggil metodeturnOn()
pada objeksmartLightDevice
dan metodeturnOffLight()
yang memanggil metodeturnOff()
pada objeksmartLightDevice
:
class SmartHome(
val smartTvDevice: SmartTvDevice,
val smartLightDevice: SmartLightDevice
) {
...
fun changeTvChannelToNext() {
smartTvDevice.nextChannel()
}
fun turnOnLight() {
smartLightDevice.turnOn()
}
fun turnOffLight() {
smartLightDevice.turnOff()
}
}
- Pada baris setelah metode
turnOffLight()
, tentukan metodeincreaseLightBrightness()
yang memanggil metodeincreaseBrightness()
di propertismartLightDevice
:
class SmartHome(
val smartTvDevice: SmartTvDevice,
val smartLightDevice: SmartLightDevice
) {
...
fun changeTvChannelToNext() {
smartTvDevice.nextChannel()
}
fun turnOnLight() {
smartLightDevice.turnOn()
}
fun turnOffLight() {
smartLightDevice.turnOff()
}
fun increaseLightBrightness() {
smartLightDevice.increaseBrightness()
}
}
- Pada baris setelah metode
increaseLightBrightness()
, tentukan metodeturnOffAllDevices()
yang memanggil metodeturnOffTv()
danturnOffLight()
:
class SmartHome(
val smartTvDevice: SmartTvDevice,
val smartLightDevice: SmartLightDevice
) {
...
fun turnOffAllDevices() {
turnOffTv()
turnOffLight()
}
}
Mengganti metode superclass dari subclass
Seperti yang telah dibahas sebelumnya, meskipun fungsi pengaktifan dan penonaktifan didukung oleh semua perangkat smart, cara kerja fungsi tersebut berbeda. Untuk menyediakan perilaku khusus perangkat ini, Anda perlu mengganti metode turnOn()
dan turnOff()
yang ditentukan di superclass. Mengganti berarti mencegat tindakan, biasanya untuk mengambil kontrol manual. Saat Anda mengganti metode, metode di subclass akan mengganggu eksekusi metode yang ditentukan di superclass dan menyediakan eksekusinya sendiri.
Ganti metode turnOn()
dan turnOff()
class SmartDevice
:
- Di bagian isi superclass
SmartDevice
, sebelum kata kuncifun
setiap metode, tambahkan kata kunciopen
:
open class SmartDevice(val name: String, val category: String) {
var deviceStatus = "online"
open fun turnOn() {
// function body
}
open fun turnOff() {
// function body
}
}
- Dalam isi class
SmartLightDevice
, tentukan metodeturnOn()
dengan isi kosong:
class SmartLightDevice(deviceName: String, deviceCategory: String) :
SmartDevice(name = deviceName, category = deviceCategory) {
var brightnessLevel = 0
set(value) {
if (value in 0..100) {
field = value
}
}
fun increaseBrightness() {
brightnessLevel++
println("Brightness increased to $brightnessLevel.")
}
fun turnOn() {
}
}
- Di bagian isi metode
turnOn()
, atur propertideviceStatus
ke string "on
", setel propertibrightnessLevel
menjadi nilai2
, lalu tambahkan pernyataanprintln()
dan teruskan string"$name
turned
on.
The
brightness
level
is
$brightnessLevel."
:
fun turnOn() {
deviceStatus = "on"
brightnessLevel = 2
println("$name turned on. The brightness level is $brightnessLevel.")
}
- Dalam isi class
SmartLightDevice
, tentukan metodeturnOff()
dengan isi kosong:
class SmartLightDevice(deviceName: String, deviceCategory: String) :
SmartDevice(name = deviceName, category = deviceCategory) {
var brightnessLevel = 0
set(value) {
if (value in 0..100) {
field = value
}
}
fun increaseBrightness() {
brightnessLevel++
println("Brightness increased to $brightnessLevel.")
}
fun turnOn() {
deviceStatus = "on"
brightnessLevel = 2
println("$name turned on. The brightness level is $brightnessLevel.")
}
fun turnOff() {
}
}
- Di bagian isi metode
turnOff()
, atur propertideviceStatus
ke string "off
", setel propertibrightnessLevel
menjadi nilai0
, lalu tambahkan pernyataanprintln()
dan teruskan string"Smart
Light
turned
off"
:
class SmartLightDevice(deviceName: String, deviceCategory: String) :
SmartDevice(name = deviceName, category = deviceCategory) {
var brightnessLevel = 0
set(value) {
if (value in 0..100) {
field = value
}
}
fun increaseBrightness() {
brightnessLevel++
println("Brightness increased to $brightnessLevel.")
}
fun turnOn() {
deviceStatus = "on"
brightnessLevel = 2
println("$name turned on. The brightness level is $brightnessLevel.")
}
fun turnOff() {
deviceStatus = "off"
brightnessLevel = 0
println("Smart Light turned off")
}
}
- Di subclass
SmartLightDevice
, sebelum kata kuncifun
dari metodeturnOn()
danturnOff()
, tambahkan kata kuncioverride
:
class SmartLightDevice(deviceName: String, deviceCategory: String) :
SmartDevice(name = deviceName, category = deviceCategory) {
var brightnessLevel = 0
set(value) {
if (value in 0..100) {
field = value
}
}
fun increaseBrightness() {
brightnessLevel++
println("Brightness increased to $brightnessLevel.")
}
override fun turnOn() {
deviceStatus = "on"
brightnessLevel = 2
println("$name turned on. The brightness level is $brightnessLevel.")
}
override fun turnOff() {
deviceStatus = "off"
brightnessLevel = 0
println("Smart Light turned off")
}
}
Kata kunci override
memberi tahu runtime Kotlin untuk mengeksekusi kode yang disertakan dalam metode yang ditentukan di subclass.
- Dalam isi class
SmartTvDevice
, tentukan metodeturnOn()
dengan isi kosong:
class SmartTvDevice(deviceName: String, deviceCategory: String) : SmartDevice(name = deviceName, category = deviceCategory) {
var speakerVolume = 2
set(value) {
if (value in 0..100) {
field = value
}
}
var channelNumber = 1
set(value) {
if (value in 0..200) {
field = value
}
}
fun increaseSpeakerVolume() {
speakerVolume++
println("Speaker volume increased to $speakerVolume.")
}
fun nextChannel() {
channelNumber++
println("Channel number increased to $channelNumber.")
}
fun turnOn() {
}
}
- Di bagian isi metode
turnOn()
, setel propertideviceStatus
ke string "on
" dan tambahkan pernyataanprintln()
, lalu teruskan string"$name is turned on. Speaker volume is set to $speakerVolume and channel number is " + "set to $channelNumber."
:
class SmartTvDevice(deviceName: String, deviceCategory: String) : SmartDevice(name = deviceName, category = deviceCategory) {
...
fun turnOn() {
deviceStatus = "on"
println(
"$name is turned on. Speaker volume is set to $speakerVolume and channel number is " +
"set to $channelNumber."
)
}
}
- Di bagian isi class
SmartTvDevice
setelah metodeturnOn()
, tentukan metodeturnOff()
dengan isi kosong:
class SmartTvDevice(deviceName: String, deviceCategory: String) : SmartDevice(name = deviceName, category = deviceCategory) {
...
fun turnOn() {
...
}
fun turnOff() {
}
}
- Di bagian isi metode
turnOff()
, setel propertideviceStatus
ke string "off
" dan tambahkan pernyataanprintln()
, lalu teruskan string"$name
turned
off"
:
class SmartTvDevice(deviceName: String, deviceCategory: String) : SmartDevice(name = deviceName, category = deviceCategory) {
...
fun turnOn() {
...
}
fun turnOff() {
deviceStatus = "off"
println("$name turned off")
}
}
- Di class
SmartTvDevice
sebelum kata kuncifun
dari metodeturnOn()
danturnOff()
, tambahkan kata kuncioverride
:
class SmartTvDevice(deviceName: String, deviceCategory: String) :
SmartDevice(name = deviceName, category = deviceCategory) {
var speakerVolume = 2
set(value) {
if (value in 0..100) {
field = value
}
}
var channelNumber = 1
set(value) {
if (value in 0..200) {
field = value
}
}
fun increaseSpeakerVolume() {
speakerVolume++
println("Speaker volume increased to $speakerVolume.")
}
fun nextChannel() {
channelNumber++
println("Channel number increased to $channelNumber.")
}
override fun turnOn() {
deviceStatus = "on"
println(
"$name is turned on. Speaker volume is set to $speakerVolume and channel number is " +
"set to $channelNumber."
)
}
override fun turnOff() {
deviceStatus = "off"
println("$name turned off")
}
}
- Dalam fungsi
main()
, gunakan kata kuncivar
untuk menentukan variabelsmartDevice
dari jenisSmartDevice
yang membuat instance objekSmartTvDevice
yang menggunakan argumen"Android
TV"
dan argumen"Entertainment"
:
fun main() {
var smartDevice: SmartDevice = SmartTvDevice("Android TV", "Entertainment")
}
- Pada baris setelah variabel
smartDevice
, panggil metodeturnOn()
pada objeksmartDevice
:
fun main() {
var smartDevice: SmartDevice = SmartTvDevice("Android TV", "Entertainment")
smartDevice.turnOn()
}
- Jalankan kode.
Outputnya adalah sebagai berikut:
Android TV is turned on. Speaker volume is set to 2 and channel number is set to 1.
- Pada baris setelah panggilan ke metode
turnOn()
, tetapkan ulang variabelsmartDevice
untuk membuat instance classSmartLightDevice
yang menggunakan argumen"Google
Light"
dan argumen"Utility"
, lalu panggil metodeturnOn()
pada referensi objeksmartDevice
:
fun main() {
var smartDevice: SmartDevice = SmartTvDevice("Android TV", "Entertainment")
smartDevice.turnOn()
smartDevice = SmartLightDevice("Google Light", "Utility")
smartDevice.turnOn()
}
- Jalankan kode.
Outputnya adalah sebagai berikut:
Android TV is turned on. Speaker volume is set to 2 and channel number is set to 1. Google Light turned on. The brightness level is 2.
Ini adalah contoh polimorfisme. Kode memanggil metode turnOn()
pada variabel jenis SmartDevice
dan, bergantung pada nilai sebenarnya dari variabel itu, implementasi metode turnOn()
yang berbeda dapat dieksekusi.
Menggunakan kembali kode superclass di subclass dengan kata kunci super
Jika metode turnOn()
dan turnOff()
dilihat lebih teliti, Anda akan melihat adanya kesamaan dalam cara mengupdate variabel deviceStatus
setiap kali metode tersebut dipanggil di subclass SmartTvDevice
dan SmartLightDevice
: kode akan diduplikasi. Anda dapat menggunakan kembali kode tersebut saat memperbarui status di class SmartDevice
.
Untuk memanggil metode yang diganti di superclass dari subclass, Anda harus menggunakan kata kunci super
. Memanggil metode dari superclass sama halnya dengan memanggil metode dari luar class. Daripada menggunakan operator .
antara objek dan metode, Anda harus menggunakan kata kunci super
yang memberi tahu compiler Kotlin untuk memanggil metode di superclass, bukan subclass.
Sintaksis untuk memanggil metode dari superclass dimulai dengan kata kunci super
, diikuti dengan operator .
, nama fungsi, dan sepasang tanda kurung. Jika diperlukan, tanda kurung dapat menyertakan argumen. Anda dapat melihat sintaksisnya dalam diagram ini:
Gunakan kembali kode dari superclass SmartDevice
:
- Hapus pernyataan
println()
dari metodeturnOn()
danturnOff()
, lalu pindahkan kode duplikat dari subclassSmartTvDevice
danSmartLightDevice
ke superclassSmartDevice
:
open class SmartDevice(val name: String, val category: String) {
var deviceStatus = "online"
open fun turnOn() {
deviceStatus = "on"
}
open fun turnOff() {
deviceStatus = "off"
}
}
- Gunakan kata kunci
super
untuk memanggil metode dari classSmartDevice
di subclassSmartTvDevice
danSmartLightDevice
:
class SmartTvDevice(deviceName: String, deviceCategory: String) :
SmartDevice(name = deviceName, category = deviceCategory) {
var speakerVolume = 2
set(value) {
if (value in 0..100) {
field = value
}
}
var channelNumber = 1
set(value) {
if (value in 0..200) {
field = value
}
}
fun increaseSpeakerVolume() {
speakerVolume++
println("Speaker volume increased to $speakerVolume.")
}
fun nextChannel() {
channelNumber++
println("Channel number increased to $channelNumber.")
}
override fun turnOn() {
super.turnOn()
println(
"$name is turned on. Speaker volume is set to $speakerVolume and channel number is " +
"set to $channelNumber."
)
}
override fun turnOff() {
super.turnOff()
println("$name turned off")
}
}
class SmartLightDevice(deviceName: String, deviceCategory: String) :
SmartDevice(name = deviceName, category = deviceCategory) {
var brightnessLevel = 0
set(value) {
if (value in 0..100) {
field = value
}
}
fun increaseBrightness() {
brightnessLevel++
println("Brightness increased to $brightnessLevel.")
}
override fun turnOn() {
super.turnOn()
brightnessLevel = 2
println("$name turned on. The brightness level is $brightnessLevel.")
}
override fun turnOff() {
super.turnOff()
brightnessLevel = 0
println("Smart Light turned off")
}
}
Mengganti properti superclass dari subclass
Serupa dengan metode, Anda juga dapat mengganti properti dengan langkah yang sama.
Ganti properti deviceType
:
- Di superclass
SmartDevice
pada baris setelah propertideviceStatus
, gunakan kata kunciopen
danval
untuk menentukan propertideviceType
yang disetel ke string"unknown"
:
open class SmartDevice(val name: String, val category: String) {
var deviceStatus = "online"
open val deviceType = "unknown"
...
}
- Di class
SmartTvDevice
, gunakan kata kuncioverride
danval
untuk menentukan propertideviceType
yang disetel ke string"Smart
TV"
:
class SmartTvDevice(deviceName: String, deviceCategory: String) :
SmartDevice(name = deviceName, category = deviceCategory) {
override val deviceType = "Smart TV"
...
}
- Di class
SmartLightDevice
, gunakan kata kuncioverride
danval
untuk menentukan propertideviceType
yang disetel ke string"Smart
Light"
:
class SmartLightDevice(deviceName: String, deviceCategory: String) :
SmartDevice(name = deviceName, category = deviceCategory) {
override val deviceType = "Smart Light"
...
}
8. Pengubah visibilitas
Pengubah visibilitas memainkan peran penting untuk mencapai enkapsulasi:
- Di class, fungsi ini memungkinkan Anda menyembunyikan properti dan metode dari akses tidak sah di luar class.
- Dalam paket, fungsi ini memungkinkan Anda menyembunyikan class dan antarmuka dari akses tidak sah di luar paket.
Kotlin menyediakan empat pengubah visibilitas:
public
. Pengubah visibilitas default. Menjadikan deklarasi dapat diakses di mana saja. Properti dan metode yang ingin Anda gunakan di luar class ditandai sebagai publik.private
. Membuat deklarasi dapat diakses di class atau file sumber yang sama.
Mungkin ada beberapa properti dan metode yang hanya digunakan di dalam class, dan Anda tidak ingin class lain menggunakannya. Properti dan metode ini dapat ditandai dengan pengubah visibilitas private
untuk memastikan class lain tidak dapat mengaksesnya secara tidak sengaja.
protected
. Membuat deklarasi dapat diakses di subclass. Properti dan metode yang ingin Anda gunakan di class yang menentukannya dan subclass ditandai dengan pengubah visibilitasprotected
.internal
. Membuat deklarasi dapat diakses di modul yang sama. Pengubah internal serupa dengan pribadi, tetapi Anda dapat mengakses properti dan metode internal dari luar class selama dapat diakses dalam modul yang sama.
Jika Anda menentukan class, class tersebut akan terlihat secara publik dan dapat diakses oleh paket apa pun yang mengimpornya, yang berarti class tersebut bersifat publik secara default, kecuali Anda menetapkan pengubah visibilitas. Demikian pula, saat Anda mendefinisikan atau mendeklarasikan properti dan metode di class, secara default keduanya dapat diakses di luar class melalui objek class. Anda harus menentukan visibilitas kode yang tepat, terutama untuk menyembunyikan properti dan metode yang tidak perlu diakses oleh class lain.
Misalnya, pertimbangkan bagaimana mobil dapat diakses oleh pengemudi. Detail tentang bagian-bagian mobil dan cara kerja mobil secara internal disembunyikan secara default. Mobil itu harus sebisa mungkin berfungsi secara intuitif. Anda tentu tidak ingin mobil itu berfungsi sekompleks pesawat komersial, sama halnya dengan Anda yang tidak ingin developer lain atau diri Anda di masa depan bingung mengenai properti dan metode class yang harus digunakan.
Pengubah visibilitas membantu Anda menampilkan bagian kode yang relevan ke class lain dalam project Anda dan memastikan implementasi tersebut tidak dapat digunakan secara tidak sengaja, sehingga membuat kode mudah dipahami dan tidak terlalu rentan terhadap bug.
Saat mendeklarasikan class, metode, atau properti, pengubah visibilitas harus ditempatkan sebelum sintaksis deklarasi, seperti yang dapat Anda lihat dalam diagram ini:
Menentukan pengubah visibilitas untuk properti
Sintaksis untuk menentukan pengubah visibilitas untuk properti dimulai dengan pengubah private
, protected
, atau internal
, diikuti dengan sintaksis yang menentukan properti. Anda dapat melihat sintaksisnya dalam diagram ini:
Misalnya, Anda dapat melihat cara menjadikan properti deviceStatus
bersifat pribadi dalam cuplikan kode ini:
open class SmartDevice(val name: String, val category: String) {
...
private var deviceStatus = "online"
...
}
Anda juga dapat menetapkan pengubah visibilitas ke fungsi penyetel. Pengubah ditempatkan sebelum kata kunci set
. Anda dapat melihat sintaksisnya dalam diagram ini:
Untuk class SmartDevice
, nilai properti deviceStatus
harus dapat dibaca di luar class melalui objek class. Namun, hanya class dan turunannya yang dapat mengupdate atau menulis nilai tersebut. Untuk menerapkan persyaratan ini, Anda harus menggunakan pengubah protected
pada fungsi set()
properti deviceStatus
.
Gunakan pengubah protected
pada fungsi set()
properti deviceStatus
:
- Di properti
deviceStatus
superclassSmartDevice
, tambahkan pengubahprotected
ke fungsiset()
:
open class SmartDevice(val name: String, val category: String) {
...
var deviceStatus = "online"
protected set(value) {
field = value
}
...
}
Anda tidak melakukan tindakan atau pemeriksaan apa pun dalam fungsi set()
. Anda cukup menetapkan parameter value
ke variabel field
. Seperti yang telah Anda pelajari sebelumnya, ini serupa dengan implementasi default untuk penyetel properti. Dalam hal ini, Anda dapat menghilangkan tanda kurung dan isi dari fungsi set()
:
open class SmartDevice(val name: String, val category: String) {
...
var deviceStatus = "online"
protected set
...
}
- Di class
SmartHome
, tentukan propertideviceTurnOnCount
yang disetel ke nilai0
dengan fungsi penyetel pribadi:
class SmartHome(
val smartTvDevice: SmartTvDevice,
val smartLightDevice: SmartLightDevice
) {
var deviceTurnOnCount = 0
private set
...
}
- Tambahkan properti
deviceTurnOnCount
, diikuti dengan operator aritmetika++
ke metodeturnOnTv()
danturnOnLight()
, lalu tambahkan propertideviceTurnOnCount
, diikuti dengan operator aritmetika--
ke metodeturnOffTv()
danturnOffLight()
:
class SmartHome(
val smartTvDevice: SmartTvDevice,
val smartLightDevice: SmartLightDevice
) {
var deviceTurnOnCount = 0
private set
fun turnOnTv() {
deviceTurnOnCount++
smartTvDevice.turnOn()
}
fun turnOffTv() {
deviceTurnOnCount--
smartTvDevice.turnOff()
}
...
fun turnOnLight() {
deviceTurnOnCount++
smartLightDevice.turnOn()
}
fun turnOffLight() {
deviceTurnOnCount--
smartLightDevice.turnOff()
}
...
}
Pengubah visibilitas untuk metode
Sintaksis untuk menentukan pengubah visibilitas untuk metode dimulai dengan pengubah private
, protected
, atau internal
, diikuti dengan sintaksis yang menentukan metode. Anda dapat melihat sintaksisnya dalam diagram ini:
Misalnya, Anda dapat melihat cara menentukan pengubah protected
untuk metode nextChannel()
di class SmartTvDevice
dalam cuplikan kode ini:
class SmartTvDevice(deviceName: String, deviceCategory: String) :
SmartDevice(name = deviceName, category = deviceCategory) {
...
protected fun nextChannel() {
channelNumber++
println("Channel number increased to $channelNumber.")
}
...
}
Pengubah visibilitas untuk konstruktor
Sintaksis untuk menentukan pengubah visibilitas bagi konstruktor serupa dengan menentukan konstruktor utama dengan beberapa perbedaan:
- Pengubah ditentukan setelah nama class, tetapi sebelum kata kunci
constructor
. - Jika Anda perlu menentukan pengubah untuk konstruktor utama, tanda kurung dan kata kunci
constructor
harus dipertahankan meskipun tidak ada parameter.
Anda dapat melihat sintaksisnya dalam diagram ini:
Misalnya, Anda dapat melihat cara menambahkan pengubah protected
ke konstruktor SmartDevice
dalam cuplikan kode ini:
open class SmartDevice protected constructor (val name: String, val category: String) {
...
}
Pengubah visibilitas untuk class
Sintaksis untuk menentukan pengubah visibilitas bagi class dimulai dengan pengubah private
, protected
, atau internal
, diikuti dengan sintaksis yang menentukan class. Anda dapat melihat sintaksisnya dalam diagram ini:
Misalnya, Anda dapat melihat cara menentukan pengubah internal
untuk class SmartDevice
dalam cuplikan kode ini:
internal open class SmartDevice(val name: String, val category: String) {
...
}
Idealnya, Anda harus berusaha mendapatkan visibilitas properti dan metode yang ketat, jadi deklarasikan dengan pengubah private
sesering mungkin. Jika Anda tidak dapat membuatnya tetap pribadi, gunakan pengubah protected
. Jika Anda tidak dapat melindunginya, gunakan pengubah internal
. Jika Anda tidak dapat menyimpannya secara internal, gunakan pengubah public
.
Menentukan pengubah visibilitas yang sesuai
Tabel ini membantu Anda menentukan pengubah visibilitas yang sesuai berdasarkan tempat properti atau metode class atau konstruktor dapat diakses:
Pengubah | Dapat diakses di class yang sama | Dapat diakses di subclass | Dapat diakses dalam modul yang sama | Modul di luar yang dapat diakses |
| ✔ | 𝗫 | 𝗫 | 𝗫 |
| ✔ | ✔ | 𝗫 | 𝗫 |
| ✔ | ✔ | ✔ | 𝗫 |
| ✔ | ✔ | ✔ | ✔ |
Di subclass SmartTvDevice
, Anda tidak boleh mengizinkan properti speakerVolume
dan channelNumber
dikontrol dari luar class. Properti ini seharusnya hanya dikontrol melalui metode increaseSpeakerVolume()
dan nextChannel()
.
Demikian pula, dalam subclass SmartLightDevice
, properti brightnessLevel
hanya boleh dikontrol melalui metode increaseLightBrightness()
.
Tambahkan pengubah visibilitas yang sesuai ke subclass SmartTvDevice
dan SmartLightDevice
:
- Di class
SmartTvDevice
, tambahkan pengubah visibilitasprivate
ke propertispeakerVolume
danchannelNumber
:
class SmartTvDevice(deviceName: String, deviceCategory: String) :
SmartDevice(name = deviceName, category = deviceCategory) {
private var speakerVolume = 2
set(value) {
if (value in 0..100) {
field = value
}
}
private var channelNumber = 1
set(value) {
if (value in 0..200) {
field = value
}
}
...
}
- Di class
SmartLightDevice
, tambahkan pengubahprivate
ke propertibrightnessLevel
:
class SmartLightDevice(deviceName: String, deviceCategory: String) :
SmartDevice(name = deviceName, category = deviceCategory) {
...
private var brightnessLevel = 0
set(value) {
if (value in 0..100) {
field = value
}
}
...
}
9. Menentukan delegasi properti
Anda telah mempelajari di bagian sebelumnya bahwa properti di Kotlin menggunakan kolom pendukung untuk menyimpan nilainya di memori. Anda menggunakan ID field
untuk mereferensikannya.
Jika melihat kode sejauh ini, Anda dapat melihat kode duplikat untuk memeriksa apakah nilai berada dalam rentang untuk properti speakerVolume
, channelNumber
, dan brightnessLevel
di class SmartTvDevice
dan SmartLightDevice
. Anda dapat menggunakan kembali kode pemeriksaan rentang dalam fungsi penyetel dengan delegasi. Sebagai ganti penggunaan kolom, serta fungsi pengambil dan penyetel untuk mengelola nilai, delegasi akan mengelolanya.
Sintaksis untuk membuat delegasi properti dimulai dengan deklarasi variabel, diikuti dengan kata kunci by
, dan objek delegasi yang menangani fungsi pengambil dan penyetel untuk properti. Anda dapat melihat sintaksisnya dalam diagram ini:
Sebelum mengimplementasikan class tempat Anda dapat mendelegasikan implementasi, Anda harus memahami antarmuka. Antarmuka adalah kontrak yang diterapkan oleh class yang mengimplementasikannya. Antarmuka berfokus pada apa yang harus dilakukan, bukan cara melakukan tindakan. Singkatnya, antarmuka membantu Anda mencapai abstraksi.
Misalnya, sebelum membangun rumah, Anda memberi tahu arsitek tentang hal yang Anda inginkan. Anda menginginkan kamar tidur, kamar anak, ruang keluarga, dapur, dan beberapa kamar mandi. Singkatnya, Anda menentukan yang Anda inginkan dan arsitek menentukan cara mencapainya. Anda dapat melihat sintaksis untuk membuat antarmuka dalam diagram ini:
Anda telah mempelajari cara memperluas class dan mengganti fungsinya. Dengan antarmuka, class mengimplementasikan antarmuka. Class ini memberikan detail implementasi untuk metode dan properti yang dideklarasikan di antarmuka. Anda akan melakukan sesuatu yang serupa dengan antarmuka ReadWriteProperty
untuk membuat delegasi. Anda akan mempelajari lebih lanjut tentang antarmuka di unit berikutnya.
Untuk membuat class delegasi untuk jenis var
, Anda harus mengimplementasikan antarmuka ReadWriteProperty
. Demikian pula, Anda harus mengimplementasikan antarmuka ReadOnlyProperty
untuk jenis val
.
Buat delegasi untuk jenis var
:
- Sebelum fungsi
main()
, buat classRangeRegulator
yang mengimplementasikan antarmukaReadWriteProperty<Any?,
Int>
:
class RangeRegulator() : ReadWriteProperty<Any?, Int> {
}
fun main() {
...
}
Jangan khawatir tentang kurung sudut atau konten di dalamnya. Keduanya merepresentasikan jenis generik dan Anda akan mempelajarinya di unit berikutnya.
- Di konstruktor utama class
RangeRegulator
, tambahkan parameterinitialValue
, propertiminValue
pribadi, dan propertimaxValue
pribadi, semuanya menggunakan jenisInt
:
class RangeRegulator(
initialValue: Int,
private val minValue: Int,
private val maxValue: Int
) : ReadWriteProperty<Any?, Int> {
}
- Dalam isi class
RangeRegulator
, ganti metodegetValue()
dansetValue()
:
class RangeRegulator(
initialValue: Int,
private val minValue: Int,
private val maxValue: Int
) : ReadWriteProperty<Any?, Int> {
override fun getValue(thisRef: Any?, property: KProperty<*>): Int {
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
}
}
Metode ini bertindak sebagai fungsi pengambil dan penyetel properti.
- Pada baris sebelum class
SmartDevice
, impor antarmukaReadWriteProperty
danKProperty
:
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
open class SmartDevice(val name: String, val category: String) {
...
}
...
class RangeRegulator(
initialValue: Int,
private val minValue: Int,
private val maxValue: Int
) : ReadWriteProperty<Any?, Int> {
override fun getValue(thisRef: Any?, property: KProperty<*>): Int {
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
}
}
...
- Di class
RangeRegulator
, pada baris sebelum metodegetValue()
, tentukan propertifieldData
dan lakukan inisialisasi properti dengan parameterinitialValue
:
class RangeRegulator(
initialValue: Int,
private val minValue: Int,
private val maxValue: Int
) : ReadWriteProperty<Any?, Int> {
var fieldData = initialValue
override fun getValue(thisRef: Any?, property: KProperty<*>): Int {
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
}
}
Properti ini bertindak sebagai kolom pendukung untuk variabel.
- Dalam isi metode
getValue()
, tampilkan propertifieldData
:
class RangeRegulator(
initialValue: Int,
private val minValue: Int,
private val maxValue: Int
) : ReadWriteProperty<Any?, Int> {
var fieldData = initialValue
override fun getValue(thisRef: Any?, property: KProperty<*>): Int {
return fieldData
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
}
}
- Dalam isi metode
setValue()
, periksa apakah parametervalue
yang ditetapkan berada dalam rentangminValue..maxValue
sebelum Anda menetapkannya ke propertifieldData
:
class RangeRegulator(
initialValue: Int,
private val minValue: Int,
private val maxValue: Int
) : ReadWriteProperty<Any?, Int> {
var fieldData = initialValue
override fun getValue(thisRef: Any?, property: KProperty<*>): Int {
return fieldData
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
if (value in minValue..maxValue) {
fieldData = value
}
}
}
- Di class
SmartTvDevice
, gunakan class delegasi untuk menentukan propertispeakerVolume
danchannelNumber
:
class SmartTvDevice(deviceName: String, deviceCategory: String) :
SmartDevice(name = deviceName, category = deviceCategory) {
override val deviceType = "Smart TV"
private var speakerVolume by RangeRegulator(initialValue = 2, minValue = 0, maxValue = 100)
private var channelNumber by RangeRegulator(initialValue = 1, minValue = 0, maxValue = 200)
...
}
- Di class
SmartLightDevice
, gunakan class delegasi untuk menentukan propertibrightnessLevel
:
class SmartLightDevice(deviceName: String, deviceCategory: String) :
SmartDevice(name = deviceName, category = deviceCategory) {
override val deviceType = "Smart Light"
private var brightnessLevel by RangeRegulator(initialValue = 0, minValue = 0, maxValue = 100)
...
}
10. Menguji solusi
Anda dapat melihat kode solusi dalam cuplikan kode ini:
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
open class SmartDevice(val name: String, val category: String) {
var deviceStatus = "online"
protected set
open val deviceType = "unknown"
open fun turnOn() {
deviceStatus = "on"
}
open fun turnOff() {
deviceStatus = "off"
}
}
class SmartTvDevice(deviceName: String, deviceCategory: String) :
SmartDevice(name = deviceName, category = deviceCategory) {
override val deviceType = "Smart TV"
private var speakerVolume by RangeRegulator(initialValue = 2, minValue = 0, maxValue = 100)
private var channelNumber by RangeRegulator(initialValue = 1, minValue = 0, maxValue = 200)
fun increaseSpeakerVolume() {
speakerVolume++
println("Speaker volume increased to $speakerVolume.")
}
fun nextChannel() {
channelNumber++
println("Channel number increased to $channelNumber.")
}
override fun turnOn() {
super.turnOn()
println(
"$name is turned on. Speaker volume is set to $speakerVolume and channel number is " +
"set to $channelNumber."
)
}
override fun turnOff() {
super.turnOff()
println("$name turned off")
}
}
class SmartLightDevice(deviceName: String, deviceCategory: String) :
SmartDevice(name = deviceName, category = deviceCategory) {
override val deviceType = "Smart Light"
private var brightnessLevel by RangeRegulator(initialValue = 0, minValue = 0, maxValue = 100)
fun increaseBrightness() {
brightnessLevel++
println("Brightness increased to $brightnessLevel.")
}
override fun turnOn() {
super.turnOn()
brightnessLevel = 2
println("$name turned on. The brightness level is $brightnessLevel.")
}
override fun turnOff() {
super.turnOff()
brightnessLevel = 0
println("Smart Light turned off")
}
}
class SmartHome(
val smartTvDevice: SmartTvDevice,
val smartLightDevice: SmartLightDevice
) {
var deviceTurnOnCount = 0
private set
fun turnOnTv() {
deviceTurnOnCount++
smartTvDevice.turnOn()
}
fun turnOffTv() {
deviceTurnOnCount--
smartTvDevice.turnOff()
}
fun increaseTvVolume() {
smartTvDevice.increaseSpeakerVolume()
}
fun changeTvChannelToNext() {
smartTvDevice.nextChannel()
}
fun turnOnLight() {
deviceTurnOnCount++
smartLightDevice.turnOn()
}
fun turnOffLight() {
deviceTurnOnCount--
smartLightDevice.turnOff()
}
fun increaseLightBrightness() {
smartLightDevice.increaseBrightness()
}
fun turnOffAllDevices() {
turnOffTv()
turnOffLight()
}
}
class RangeRegulator(
initialValue: Int,
private val minValue: Int,
private val maxValue: Int
) : ReadWriteProperty<Any?, Int> {
var fieldData = initialValue
override fun getValue(thisRef: Any?, property: KProperty<*>): Int {
return fieldData
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
if (value in minValue..maxValue) {
fieldData = value
}
}
}
fun main() {
var smartDevice: SmartDevice = SmartTvDevice("Android TV", "Entertainment")
smartDevice.turnOn()
smartDevice = SmartLightDevice("Google Light", "Utility")
smartDevice.turnOn()
}
Outputnya adalah sebagai berikut:
Android TV is turned on. Speaker volume is set to 2 and channel number is set to 1. Google Light turned on. The brightness level is 2.
11. Coba tantangan ini
- Di class
SmartDevice
, tentukan metodeprintDeviceInfo()
yang mencetak string"Device
name:
$name,
category:
$category,
type:
$deviceType"
. - Di class
SmartTvDevice
, tentukan metodedecreaseVolume()
yang menurunkan volume dan metodepreviousChannel()
yang membuka saluran sebelumnya. - Di class
SmartLightDevice
, tentukan metodedecreaseBrightness()
yang mengurangi kecerahan. - Di class
SmartHome
, pastikan bahwa semua tindakan hanya dapat dilakukan jika propertideviceStatus
setiap perangkat disetel ke string"on"
. Selain itu, pastikan propertideviceTurnOnCount
diupdate dengan benar.
Setelah Anda selesai mengimplementasikan:
- Di class
SmartHome
, tentukan metodedecreaseTvVolume()
,changeTvChannelToPrevious()
,printSmartTvInfo()
,printSmartLightInfo()
, dandecreaseLightBrightness()
. - Panggil metode yang sesuai dari class
SmartTvDevice
danSmartLightDevice
di classSmartHome
. - Dalam fungsi
main()
, panggil metode yang ditambahkan ini untuk mengujinya.
12. Kesimpulan
Selamat! Anda telah mempelajari cara menentukan class dan membuat instance objek. Anda juga telah mempelajari cara membuat hubungan antar-class dan membuat delegasi properti.
Ringkasan
- Ada empat prinsip utama OOP: enkapsulasi, abstraksi, pewarisan, dan polimorfisme.
- Class ditentukan dengan kata kunci
class
, serta berisi properti dan metode. - Properti serupa dengan variabel, kecuali properti dapat memiliki pengambil dan penyetel kustom.
- Konstruktor menentukan cara membuat instance objek class.
- Anda dapat menghilangkan kata kunci
constructor
saat menentukan konstruktor utama. - Pewarisan mempermudah penggunaan kembali kode.
- Hubungan IS-A mengacu pada pewarisan.
- Hubungan HAS-A mengacu pada komposisi.
- Pengubah visibilitas memainkan peran penting dalam pencapaian enkapsulasi.
- Kotlin menyediakan empat pengubah visibilitas: pengubah
public
,private
,protected
, daninternal
. - Delegasi properti memungkinkan Anda menggunakan kembali kode pengambil dan penyetel di beberapa class.