Class dan pewarisan di Kotlin

1. Sebelum memulai

Prasyarat

  • Telah terbiasa menggunakan Kotlin Playground untuk mengedit program Kotlin.
  • Memahami konsep dasar pemrograman di Kotlin seperti yang dijelaskan dalam Unit 1 pada kursus ini. Secara khusus, program main() berfungsi dengan argumen yang menampilkan nilai, variabel, jenis data, dan operasi, serta pernyataan if/else.
  • Mampu menetapkan class di Kotlin, membuat instance objek darinya, serta mengakses properti dan metodenya.

Yang akan Anda pelajari

  • Membuat program Kotlin yang menggunakan pewarisan untuk menerapkan hierarki class.
  • Memperluas class, mengganti fungsi yang sudah ada, dan menambahkan fungsi baru.
  • Memilih pengubah visibilitas yang benar untuk variabel.

Yang akan Anda build

  • Program Kotlin dengan berbagai jenis tempat tinggal yang diterapkan sebagai hierarki class.

Yang Anda perlukan

2. Apa yang dimaksud dengan hierarki class?

Wajar bagi manusia untuk mengklasifikasikan item yang memiliki properti dan perilaku serupa ke dalam beberapa grup dan bahkan membentuk beberapa jenis hierarki. Misalnya, Anda dapat memiliki kategori yang luas seperti sayuran, dan di dalamnya Anda dapat memiliki jenis yang lebih spesifik seperti polong-polongan. Di dalam polong-polongan, Anda dapat memiliki jenis yang lebih spesifik seperti kacang polong, kacang, lentil, chick pea, dan kedelai.

Contoh di atas dapat diwakili sebagai hierarki karena polong-polongan mengandung atau mewarisi semua sifat-sifat sayuran (misalnya, tanaman dan dapat dimakan). Demikian pula, kacang polong, kacang, dan lentil juga memiliki sifat polong-polongan ditambah sifat uniknya sendiri.

Mari lihat cara Anda mewakili hubungan ini dalam istilah pemrograman. Jika Anda menjadikan Vegetable sebagai class di Kotlin, Anda dapat membuat Legume sebagai turunan atau subclass dari class Vegetable. Ini berarti semua properti dan metode class Vegetable diwarisi oleh (artinya juga tersedia di) class Legume.

Anda dapat mewakili hal ini dalam diagram hierarki class seperti yang ditunjukkan di bawah ini. Anda dapat merujuk ke Vegetable sebagai induk atau superclass dari class Legume.

87e0a5eb0f85042d.png

Anda dapat melanjutkan dan memperluas hierarki class dengan membuat subclass Legume seperti Lentil dan Chickpea. Ini akan membuat Legume sebagai turunan atau subclass Vegetable serta induk atau superclass Lentil dan Chickpea. Vegetable adalah class root atau level atas (atau dasar) dari hierarki ini.

638655b960530d9.png

Pewarisan di Class Android

Meskipun Anda dapat menulis kode Kotlin tanpa menggunakan class, dan Anda telah melakukannya di codelab sebelumnya, ada banyak bagian Android yang diberikan kepada Anda dalam bentuk class, seperti aktivitas, tampilan, dan kelompok tampilan. Oleh karena itu, memahami hierarki class sangat penting untuk mengembangkan aplikasi Android dan memungkinkan Anda memanfaatkan fitur yang disediakan oleh framework Android.

Misalnya, ada class View di Android yang mewakili area persegi panjang di layar dan bertanggung jawab menangani gambar dan peristiwa. Class TextView adalah subclass dari class View yang artinya TextView mewarisi semua properti dan fungsi dari class View, serta menambahkan logika khusus untuk menampilkan teks kepada pengguna.

c39a8aaa5b013de8.png

Lebih jauh, class EditText dan Button adalah turunan dari class TextView. Kedua class tersebut mewarisi semua properti dan metode class TextView dan View, serta menambahkan logika spesifiknya sendiri. Misalnya, EditText menambahkan fungsinya sendiri untuk dapat mengedit teks di layar.

Daripada harus menyalin dan menempelkan semua logika dari class View dan TextView ke dalam class EditText, EditText hanya perlu membuat subclass dari class TextView yang selanjutnya akan membuat subclass untuk class View. Kemudian, kode dalam class EditText dapat secara khusus berfokus pada perbedaan komponen UI ini dengan tampilan lainnya.

Di bagian atas halaman dokumentasi untuk class Android di situs developer.android.com, Anda dapat melihat diagram hierarki class. Anda akan dapat melihat kotlin.Any di bagian atas hierarki karena di Kotlin, semua class memiliki superclass umum Any. Pelajari lebih lanjut di sini.

1ce2b1646b8064ab.png

Seperti yang Anda lihat, mempelajari cara memanfaatkan pewarisan di antara class dapat membuat kode Anda lebih mudah ditulis, digunakan kembali, dibaca, dan diuji.

3. Membuat class dasar

Hierarki class tempat tinggal

Dalam codelab ini, Anda akan mem-build program Kotlin yang menunjukkan cara kerja hierarki class, menggunakan tempat tinggal (selter hunian) yang dilengkapi ruang lantai, tingkat, dan penghuni sebagai contoh.

Di bawah ini merupakan diagram hierarki class yang akan Anda buat. Di root, Anda memiliki Dwelling yang menentukan properti dan fungsi yang berlaku untuk semua tempat tinggal, mirip dengan cetak biru. Anda juga akan memiliki class untuk kabin persegi (SquareCabin), pondok bundar (RoundHut), dan menara bundar (RoundTower) yang merupakan RoundHut dengan beberapa lantai.

de1387ca7fc26c81.png

Class yang akan diterapkan:

  • Dwelling: class dasar yang mewakili selter non-spesifik yang menyimpan informasi umum untuk semua tempat tinggal.
  • SquareCabin: kabin persegi yang terbuat dari kayu dengan area lantai persegi.
  • RoundHut: pondok bundar yang terbuat dari jerami dengan area lantai melingkar, dan induk dari RoundTower.
  • RoundTower: menara bundar yang terbuat dari batu dengan area lantai melingkar dan beberapa tingkat.

Membuat class Dwelling abstrak

Semua class dapat berupa class dasar dari hierarki class atau induk dari class lainnya.

Class "abstrak" adalah class yang tidak dapat dibuat instance-nya karena tidak diterapkan sepenuhnya. Anda bisa menganggapnya sebagai sketsa. Sketsa menggabungkan ide dan rencana akan sesuatu, tetapi tidak cukup informasi untuk membuatnya. Anda menggunakan sketsa (class abstrak) untuk membuat cetak biru (class) tempat Anda membuat instance objek yang sebenarnya.

Manfaat umum pembuatan superclass adalah untuk menampung properti dan fungsi yang sama untuk semua subclass-nya. Jika nilai properti dan implementasi fungsi tidak diketahui, buatlah abstrak class. Misalnya, Vegetables memiliki banyak properti yang sama dengan semua sayuran, tetapi Anda tidak dapat membuat instance sayuran non-spesifik karena tidak tahu bentuk atau warnanya. Jadi, Vegetable adalah class abstrak yang membebaskan subclass untuk menentukan detail spesifik setiap sayuran.

Deklarasi class abstrak dimulai dengan kata kunci abstract.

Dwelling akan menjadi class abstrak seperti Vegetable. Contoh class tersebut akan berisi properti dan fungsi umum untuk banyak jenis tempat tinggal, tetapi nilai properti dan detail penerapan fungsi yang tepat tidak diketahui.

  1. Buka Kotlin Playground di https://developer.android.com/training/kotlinplayground.
  2. Di editor, hapus println("Hello, world!") di dalam fungsi main().
  3. Lalu, tambahkan kode ini di bawah fungsi main() untuk membuat class abstract yang disebut Dwelling.
abstract class Dwelling(){
}

Menambahkan properti bahan bangunan

Di class Dwelling ini, Anda menentukan berbagai hal yang benar untuk semua tempat tinggal, meskipun hal tersebut mungkin berbeda untuk tempat tinggal lain. Semua tempat tinggal terbuat dari bahan bangunan.

  1. Di dalam Dwelling, buat variabel buildingMaterial jenis String untuk mewakili bahan bangunan. Bahan bangunan tidak pernah akan berubah. Jadi, gunakan val untuk menjadikannya sebagai variabel yang tidak dapat diubah.
val buildingMaterial: String
  1. Jalankan program Anda dan error ini akan muncul.
Property must be initialized or be abstract

Properti buildingMaterial tidak memiliki nilai. Bahkan, Anda TIDAK dapat menilainya karena bangunan non-spesifik tidak dibuat dari sesuatu yang spesifik. Jadi, seperti yang ditunjukkan oleh pesan error, Anda dapat mengawali deklarasi buildingMaterial dengan kata kunci abstract untuk menunjukkan bahwa bahan bangunan tidak akan ditentukan di sini.

  1. Tambahkan kata kunci abstract ke dalam definisi variabel.
abstract val buildingMaterial: String
  1. Jalankan kode Anda, dan meskipun tidak melakukan apa pun, kode tersebut akan dikompilasi tanpa error.
  2. Buat instance Dwelling dalam fungsi main() dan jalankan kode Anda.
val dwelling = Dwelling()
  1. Anda akan mendapatkan error karena Anda tidak dapat membuat instance class Dwelling abstrak.
Cannot create an instance of an abstract class
  1. Hapus kode yang salah ini.

Kode akhir Anda sejauh ini:

abstract class Dwelling(){
    abstract val buildingMaterial: String
}

Menambahkan properti kapasitas

Properti lain dari tempat tinggal adalah kapasitas, yaitu jumlah orang yang dapat tinggal di dalamnya.

Semua tempat tinggal memiliki kapasitas yang tidak berubah. Namun, kapasitas tidak dapat ditetapkan dalam superclass Dwelling. Kapasitas harus didefinisikan dalam subclass untuk jenis tempat tinggal tertentu.

  1. Dalam Dwelling, tambahkan bilangan bulat abstract val yang disebut capacity.
abstract val capacity: Int

Menambahkan properti pribadi untuk jumlah penghuni

Semua tempat tinggal akan memiliki jumlah residents yang menghuni tempat tinggal (jumlahnya bisa kurang dari atau sama dengan capacity). Jadi, tentukan properti residents dalam superclass Dwelling untuk semua subclass yang akan diwarisi dan digunakan.

  1. Anda dapat membuat parameter residents yang diteruskan ke konstruktor class Dwelling. Properti residents adalah var karena jumlah penghuni dapat berubah setelah instance dibuat.
abstract class Dwelling(private var residents: Int) {

Perhatikan bahwa properti residents ditandai dengan kata kunci private. Private merupakan pengubah visibilitas di Kotlin yang berarti bahwa properti residents hanya terlihat oleh (dan dapat digunakan di dalam) class ini. Kode ini tidak dapat diakses dari tempat lain dalam program Anda. Anda dapat menandai properti atau metode dengan kata kunci private. Sebaliknya, jika tidak ada pengubah visibilitas yang ditentukan, properti dan metode akan bersifat public secara default dan dapat diakses dari bagian lain program Anda. Jumlah orang yang menghuni tempat tinggal biasanya merupakan informasi pribadi (dibandingkan dengan informasi bahan bangunan atau kapasitas bangunan) dan ini adalah keputusan yang wajar.

Dengan capacity dari tempat tinggal dan jumlah residents saat ini yang ditentukan, Anda dapat membuat fungsi hasRoom() untuk menentukan ketersediaan ruang untuk penghuni lain dalam tempat tinggal. Anda dapat menentukan dan mengimplementasikan fungsi hasRoom() dalam class Dwelling karena penghitungan ketersediaan ruang menggunakan formula yang sama untuk semua tempat tinggal. Ada ruang di dalam Dwelling jika jumlah residents kurang dari capacity, dan fungsi ini akan menampilkan true atau false berdasarkan perbandingan ini.

  1. Tambahkan fungsi hasRoom() ke class Dwelling.
fun hasRoom(): Boolean {
    return residents < capacity
}
  1. Anda dapat menjalankan kode ini dan seharusnya tidak akan ada error. Kode belum melakukan apa pun yang tampak.

Kode yang sudah selesai akan terlihat seperti ini:

abstract class Dwelling(private var residents: Int) {

   abstract val buildingMaterial: String
   abstract val capacity: Int

   fun hasRoom(): Boolean {
       return residents < capacity
   }
}

4. Membuat subclass

Membuat subclass SquareCabin

  1. Di bawah class Dwelling, buat class dengan nama SquareCabin.
class SquareCabin
  1. Selanjutnya, Anda harus menunjukkan bahwa SquareCabin berkaitan dengan Dwelling. Dalam kode, Anda ingin menunjukkan bahwa SquareCabin diperluas dari Dwelling (atau merupakan subclass untuk Dwelling) karena SquareCabin akan menyediakan implementasi untuk bagian abstrak Dwelling.

Tunjukkan hubungan pewarisan ini dengan menambahkan titik dua (:) setelah nama class SquareCabin, lalu diikuti dengan panggilan untuk menginisialisasi class induk Dwelling. Jangan lupa tambahkan tanda kurung setelah nama class Dwelling.

class SquareCabin : Dwelling()
  1. Saat memperluas dari superclass, Anda harus meneruskan parameter wajib yang diminta oleh superclass. Dwelling memerlukan jumlah residents sebagai input. Anda dapat meneruskan jumlah penghuni tetap seperti 3.
class SquareCabin : Dwelling(3)

Namun, Anda ingin program menjadi lebih fleksibel dan memungkinkan jumlah variabel penghuni untuk SquareCabins. Oleh karena itu, buat parameter residents dengan definisi class SquareCabin. Jangan mendeklarasikan residents sebagai val, karena Anda menggunakan kembali properti yang sudah dinyatakan di class induk Dwelling.

class SquareCabin(residents: Int) : Dwelling(residents)
  1. Jalankan kode.
  2. Ini akan menyebabkan error. Lihat:
Class 'SquareCabin' is not abstract and does not implement abstract base class member public abstract val buildingMaterial: String defined in Dwelling

Saat Anda mendeklarasikan fungsi dan variabel abstrak, deklarasi ini akan menyerupai promise yang Anda beri nilai dan implementasinya nanti. Untuk variabel, ini artinya setiap subclass dari class abstrak tersebut harus memberikan nilai. Untuk fungsi, ini artinya setiap subclass perlu menerapkan isi fungsi.

Dalam class Dwelling, Anda menetapkan variabel abstract buildingMaterial. SquareCabin adalah subclass dari Dwelling sehingga harus memberikan nilai untuk buildingMaterial. Gunakan kata kunci override untuk menunjukkan bahwa properti ini telah ditetapkan di class induk dan akan diganti di class ini.

  1. Dalam class SquareCabin, override properti buildingMaterial dan isi nilainya dengan "Wood".
  2. Lakukan hal yang sama untuk capacity yang menyebutkan 6 penghuni dapat tinggal di SquareCabin.
class SquareCabin(residents: Int) : Dwelling(residents) {
    override val buildingMaterial = "Wood"
    override val capacity = 6
}

Kode yang sudah selesai akan terlihat seperti ini.

abstract class Dwelling(private var residents: Int) {
    abstract val buildingMaterial: String
    abstract val capacity: Int

    fun hasRoom(): Boolean {
       return residents < capacity
   }
}

class SquareCabin(residents: Int) : Dwelling(residents) {
    override val buildingMaterial = "Wood"
    override val capacity = 6
}

Untuk menguji kode Anda, buat instance SquareCabin di dalam program.

Menggunakan SquareCabin

  1. Sisipkan fungsi main() kosong sebelum definisi class Dwelling dan SquareCabin.
fun main() {

}

abstract class Dwelling(private var residents: Int) {
    ...
}

class SquareCabin(residents: Int) : Dwelling(residents) {
    ...
}
  1. Dalam fungsi main(), buat instance SquareCabin bernama squareCabin dengan 6 penghuni. Tambahkan pernyataan cetak untuk bahan bangunan, kapasitas, dan fungsi hasRoom().
fun main() {
    val squareCabin = SquareCabin(6)

    println("\nSquare Cabin\n============")
    println("Capacity: ${squareCabin.capacity}")
    println("Material: ${squareCabin.buildingMaterial}")
    println("Has room? ${squareCabin.hasRoom()}")
}

Perhatikan bahwa fungsi hasRoom() tidak ditentukan di class SquareCabin, tetapi ditentukan di class Dwelling. Karena SquareCabin adalah subclass dari class Dwelling, fungsi hasRoom() diwarisi secara cuma-cuma. Fungsi hasRoom() sekarang dapat dipanggil di semua instance SquareCabin, seperti yang terlihat dalam cuplikan kode sebagai squareCabin.hasRoom().

  1. Jalankan kode Anda, dan kode tersebut akan mencetak perintah berikut ini:
Square Cabin
============
Capacity: 6
Material: Wood
Has room? false

Anda membuat squareCabin dengan 6 penghuni yang setara dengan capacity sehingga hasRoom() menampilkan false. Anda dapat bereksperimen dengan menginisialisasi SquareCabin dengan jumlah residents yang lebih kecil, dan saat Anda menjalankan program lagi, hasRoom() harus menampilkan true.

Menggunakan with untuk menyederhanakan kode Anda

Dalam pernyataan println(), setiap kali Anda mereferensikan properti atau fungsi squareCabin, perhatikan bagaimana Anda mengulangi squareCabin. Hal ini menjadi berulang-ulang dan dapat menjadi sumber error saat Anda menyalin dan menempelkan pernyataan cetak.

Saat Anda bekerja dengan instance class tertentu dan harus mengakses beberapa properti dan fungsi instance tersebut, Anda dapat mengucapkan "lakukan semua operasi berikut dengan objek instance ini" menggunakan pernyataan with. Mulai dengan kata kunci with, diikuti dengan nama instance dalam tanda kurung, lalu diikuti dengan tanda kurung kurawal yang berisi operasi yang ingin Anda lakukan.

with (instanceName) {
    // all operations to do with instanceName
}
  1. Dalam fungsi main(), ubah pernyataan cetak untuk menggunakan with.
  2. Hapus squareCabin. di laporan cetak.
with(squareCabin) {
    println("\nSquare Cabin\n============")
    println("Capacity: ${capacity}")
    println("Material: ${buildingMaterial}")
    println("Has room? ${hasRoom()}")
}
  1. Jalankan kode Anda lagi untuk memastikan kode berjalan tanpa error dan menampilkan output yang sama.
Square Cabin
============
Capacity: 6
Material: Wood
Has room? false

Ini adalah kode lengkap Anda:

fun main() {
    val squareCabin = SquareCabin(6)

    with(squareCabin) {
        println("\nSquare Cabin\n============")
        println("Capacity: ${capacity}")
        println("Material: ${buildingMaterial}")
        println("Has room? ${hasRoom()}")
    }
}

abstract class Dwelling(private var residents: Int) {
    abstract val buildingMaterial: String
    abstract val capacity: Int

    fun hasRoom(): Boolean {
       return residents < capacity
   }
}

class SquareCabin(residents: Int) : Dwelling(residents) {
    override val buildingMaterial = "Wood"
    override val capacity = 6
}

Membuat subclass RoundHut

  1. Dengan cara yang sama seperti SquareCabin, tambahkan subclass lain, RoundHut, ke Dwelling.
  2. Ganti buildingMaterial dan beri nilai "Straw".
  3. Ganti capacity dan tetapkan ke 4.
class RoundHut(residents: Int) : Dwelling(residents) {
    override val buildingMaterial = "Straw"
    override val capacity = 4
}
  1. Di main(), buat instance RoundHut dengan 3 penghuni.
val roundHut = RoundHut(3)
  1. Tambahkan kode di bawah untuk mencetak informasi tentang roundHut.
with(roundHut) {
    println("\nRound Hut\n=========")
    println("Material: ${buildingMaterial}")
    println("Capacity: ${capacity}")
    println("Has room? ${hasRoom()}")
}
  1. Jalankan kode Anda, dan output untuk seluruh program seharusnya akan seperti berikut:
Square Cabin
============
Capacity: 6
Material: Wood
Has room? false

Round Hut
=========
Material: Straw
Capacity: 4
Has room? true

Anda sekarang memiliki hierarki class yang terlihat seperti ini, dengan Dwelling sebagai class root serta SquareCabin dan RoundHut sebagai subclass dari Dwelling.

c19084f4a83193a0.png

Membuat subclass RoundTower

Class terakhir dalam hierarki class ini adalah menara bundar. Anggap menara bundar sebagai pondok bulat yang terbuat dari batu dan memiliki banyak lantai. Jadi, Anda dapat membuat RoundTower sebagai subclass dari RoundHut.

  1. Buat class RoundTower yang merupakan subclass dari RoundHut. Tambahkan parameter residents ke konstruktor RoundTower, lalu teruskan parameter tersebut ke konstruktor superclass RoundHut.
  2. Ganti buildingMaterial menjadi "Stone".
  3. Tetapkan capacity ke 4.
class RoundTower(residents: Int) : RoundHut(residents) {
    override val buildingMaterial = "Stone"
    override val capacity = 4
}
  1. Jalankan kode ini dan Anda akan mendapatkan pesan error.
This type is final, so it cannot be inherited from

Error ini berarti bahwa class RoundHut tidak dapat dibuatkan subclass-nya (atau diwariskan). Secara default, di Kotlin, class bersifat final dan tidak dapat dibuatkan subclass-nya. Anda hanya diizinkan untuk mewarisi dari class abstract atau class yang ditandai dengan kata kunci open. Oleh karena itu, Anda harus menandai class RoundHut dengan kata kunci open agar dapat diwariskan.

  1. Tambahkan kata kunci open di awal deklarasi RoundHut.
open class RoundHut(residents: Int) : Dwelling(residents) {
   override val buildingMaterial = "Straw"
   override val capacity = 4
}
  1. Di main(), buat instance roundTower dan cetak informasinya.
 val roundTower = RoundTower(4)
with(roundTower) {
    println("\nRound Tower\n==========")
    println("Material: ${buildingMaterial}")
    println("Capacity: ${capacity}")
    println("Has room? ${hasRoom()}")
}

Kode lengkapnya sebagai berikut.

fun main() {
    val squareCabin = SquareCabin(6)
    val roundHut = RoundHut(3)
    val roundTower = RoundTower(4)

    with(squareCabin) {
        println("\nSquare Cabin\n============")
        println("Capacity: ${capacity}")
        println("Material: ${buildingMaterial}")
        println("Has room? ${hasRoom()}")
    }

    with(roundHut) {
        println("\nRound Hut\n=========")
        println("Material: ${buildingMaterial}")
        println("Capacity: ${capacity}")
        println("Has room? ${hasRoom()}")
    }

    with(roundTower) {
        println("\nRound Tower\n==========")
        println("Material: ${buildingMaterial}")
        println("Capacity: ${capacity}")
        println("Has room? ${hasRoom()}")
    }
}

abstract class Dwelling(private var residents: Int) {
    abstract val buildingMaterial: String
    abstract val capacity: Int

    fun hasRoom(): Boolean {
       return residents < capacity
   }
}

class SquareCabin(residents: Int) : Dwelling(residents) {
    override val buildingMaterial = "Wood"
    override val capacity = 6
}

open class RoundHut(residents: Int) : Dwelling(residents) {
   override val buildingMaterial = "Straw"
   override val capacity = 4
}

class RoundTower(residents: Int) : RoundHut(residents) {
    override val buildingMaterial = "Stone"
    override val capacity = 4
}
  1. Jalankan kode. Seharusnya sekarang kode dapat berfungsi tanpa error dan menghasilkan output berikut.
Square Cabin
============
Capacity: 6
Material: Wood
Has room? false

Round Hut
=========
Material: Straw
Capacity: 4
Has room? true

Round Tower
==========
Material: Stone
Capacity: 4
Has room? false

Menambahkan beberapa lantai ke RoundTower

Berdasarkan implikasinya, RoundHut merupakan bangunan satu lantai. Menara biasanya memiliki beberapa tingkat (lantai).

Dengan mempertimbangkan kapasitas, makin banyak lantai yang dimiliki sebuah menara, makin besar kapasitas yang seharusnya dimiliki.

Anda dapat mengubah RoundTower agar memiliki beberapa lantai dan menyesuaikan kapasitasnya berdasarkan jumlah lantai.

  1. Perbarui konstruktor RoundTower untuk mengambil parameter bilangan bulat tambahan val floors untuk jumlah lantai. Tempatkan setelah residents. Perhatikan bahwa Anda tidak perlu meneruskan ini ke konstruktor RoundHut induk karena floors ditentukan di sini dalam RoundTower dan RoundHut tidak memiliki floors.
class RoundTower(
    residents: Int,
    val floors: Int) : RoundHut(residents) {

    ...
}
  1. Jalankan kode. Terjadi error saat membuat roundTower dalam metode main() karena Anda tidak memberikan angka untuk argumen floors. Anda dapat menambahkan argumen yang tidak ada.

Atau, dalam definisi class RoundTower, Anda dapat menambahkan nilai default untuk floors seperti yang ditunjukkan di bawah ini. Kemudian, jika tidak ada nilai untuk floors yang diteruskan ke dalam konstruktor, nilai default dapat digunakan untuk membuat instance objek.

  1. Dalam kode Anda, tambahkan = 2 setelah pernyataan floors untuk menetapkan nilai default 2.
class RoundTower(
    residents: Int,
    val floors: Int = 2) : RoundHut(residents) {

    ...
}
  1. Jalankan kode. Kode harus dikompilasi karena RoundTower(4) sekarang membuat instance objek RoundTower dengan nilai default 2 lantai.
  2. Di class RoundTower, perbarui capacity untuk mengalikannya dengan jumlah lantai.
override val capacity = 4 * floors
  1. Jalankan kode Anda dan perhatikan bahwa kapasitas RoundTower sekarang menjadi 8 untuk 2 lantai.

Berikut adalah kode yang sudah selesai.

fun main() {

    val squareCabin = SquareCabin(6)
    val roundHut = RoundHut(3)
    val roundTower = RoundTower(4)

    with(squareCabin) {
        println("\nSquare Cabin\n============")
        println("Capacity: ${capacity}")
        println("Material: ${buildingMaterial}")
        println("Has room? ${hasRoom()}")
    }

    with(roundHut) {
        println("\nRound Hut\n=========")
        println("Material: ${buildingMaterial}")
        println("Capacity: ${capacity}")
        println("Has room? ${hasRoom()}")
    }

    with(roundTower) {
        println("\nRound Tower\n==========")
        println("Material: ${buildingMaterial}")
        println("Capacity: ${capacity}")
        println("Has room? ${hasRoom()}")
    }
}

abstract class Dwelling(private var residents: Int) {
    abstract val buildingMaterial: String
    abstract val capacity: Int

    fun hasRoom(): Boolean {
       return residents < capacity
   }
}

class SquareCabin(residents: Int) : Dwelling(residents) {
    override val buildingMaterial = "Wood"
    override val capacity = 6
}

open class RoundHut(residents: Int) : Dwelling(residents) {
    override val buildingMaterial = "Straw"
    override val capacity = 4
}

class RoundTower(
    residents: Int,
    val floors: Int = 2) : RoundHut(residents) {

    override val buildingMaterial = "Stone"
    override val capacity = 4 * floors
}

5. Mengubah class dalam hierarki

Menghitung luas lantai

Dalam latihan ini, Anda akan mempelajari cara mendeklarasikan fungsi abstrak di class abstrak, lalu menerapkan fungsinya di subclass.

Semua tempat tinggal memiliki luas lantai, tetapi penghitungannya berbeda-beda menyesuaikan bentuk tempat tinggal.

Menentukan floorArea() di class Dwelling

  1. Pertama-tama, tambahkan fungsi abstract floorArea() ke class Dwelling. Tampilkan Double. Double adalah jenis data, seperti String dan Int; data ini digunakan untuk bilangan floating point, yaitu bilangan yang memiliki titik desimal yang diikuti oleh bagian pecahan, seperti 5,8793.
abstract fun floorArea(): Double

Semua metode abstrak yang didefinisikan dalam class abstrak harus diterapkan di salah satu subclass-nya. Sebelum dapat menjalankan kode, Anda perlu mengimplementasikan floorArea() di subclass.

Mengimplementasikan floorArea() untuk SquareCabin

Seperti pada buildingMaterial dan capacity, karena mengimplementasikan fungsi abstract yang ditentukan di class induk, Anda harus menggunakan kata kunci override.

  1. Di class SquareCabin, mulai dengan kata kunci override lalu diikuti dengan penerapan fungsi floorArea() yang sebenarnya seperti yang ditunjukkan di bawah ini.
override fun floorArea(): Double {

}
  1. Tampilkan luas lantai yang dihitung. Luas persegi panjang atau persegi adalah panjang sisi yang dikalikan dengan panjang sisi lainnya. Isi fungsinya adalah return length * length.
override fun floorArea(): Double {
    return length * length
}

Panjang ini bukanlah variabel dalam class dan berbeda untuk setiap instance sehingga Anda dapat menambahkannya sebagai parameter konstruktor untuk class SquareCabin.

  1. Ubah definisi class SquareCabin untuk menambahkan parameter length jenis Double. Nyatakan properti sebagai val karena panjang bangunan tidak berubah.
class SquareCabin(residents: Int, val length: Double) : Dwelling(residents) {

Dwelling dan semua subclass-nya memiliki residents sebagai argumen konstruktor. Argumen tersebut merupakan argumen pertama dalam konstruktor Dwelling. Jadi, sebaiknya Anda juga menjadikannya sebagai argumen pertama di semua konstruktor subclass dan menempatkan argumen dalam urutan yang sama di semua definisi class. Oleh karena itu, masukkan parameter length baru setelah parameter residents.

  1. Dalam main(), perbarui pembuatan instance squareCabin. Teruskan 50.0 ke konstruktor SquareCabin sebagai length.
val squareCabin = SquareCabin(6, 50.0)
  1. Dalam pernyataan with untuk squareCabin, tambahkan pernyataan cetak untuk luas lantai.
println("Floor area: ${floorArea()}")

Kode tidak akan berjalan karena Anda juga harus mengimplementasikan floorArea() dalam RoundHut.

Mengimplementasikan floorArea() untuk RoundHut

Dengan cara yang sama, implementasikan luas lantai untuk RoundHut. RoundHut juga merupakan subclass langsung dari Dwelling sehingga Anda harus menggunakan kata kunci override.

Luas lantai tempat tinggal melingkar adalah PI * radius * radius.

PI adalah nilai matematika. Nilai ini ditentukan dalam library matematika. Library adalah kumpulan fungsi dan nilai yang telah ditentukan sebelumnya dan ditetapkan di luar program yang dapat digunakan oleh program. Untuk menggunakan fungsi atau nilai library, Anda harus memberi tahu compiler bahwa Anda akan menggunakannya. Anda melakukannya dengan mengimpor fungsi atau nilai ke dalam program. Untuk menggunakan PI dalam program, Anda perlu mengimpor kotlin.math.PI.

  1. Impor PI dari library matematika Kotlin. Letakkan ini di bagian atas file, sebelum main().
import kotlin.math.PI
  1. Implementasikan fungsi floorArea() untuk RoundHut.
override fun floorArea(): Double {
    return PI * radius * radius
}

Peringatan: Jika tidak mengimpor kotlin.math.PI, Anda akan mendapatkan error, jadi impor library ini sebelum menggunakannya. Atau, Anda dapat menulis versi PI yang sepenuhnya memenuhi syarat, seperti dalam kotlin.math.PI * radius * radius, dan kemudian pernyataan impor tidak diperlukan.

  1. Perbarui konstruktor RoundHut untuk memasukkan radius.
open class RoundHut(
   residents: Int,
   val radius: Double) : Dwelling(residents) {
  1. Dalam main(), perbarui inisialisasi roundHut dengan meneruskan radius dari 10.0 ke konstruktor RoundHut.
val roundHut = RoundHut(3, 10.0)
  1. Tambahkan pernyataan cetak di dalam pernyataan with untuk roundHut.
println("Floor area: ${floorArea()}")

Mengimplementasikan floorArea() untuk RoundTower

Kode Anda belum berjalan, dan gagal dengan menampilkan error ini:

Error: No value passed for parameter 'radius'

Dalam RoundTower, agar program dapat dikompilasi, Anda tidak perlu mengimplementasikan floorArea() karena akan diwarisi dari RoundHut, tetapi Anda perlu memperbarui definisi class RoundTower agar memiliki argumen radius yang sama dengan induknya, RoundHut.

  1. Ubah konstruktor RoundTower untuk menggunakan radius. Masukkan radius setelah residents dan sebelum floors. Sebaiknya variabel dengan nilai default dicantumkan di bagian akhir. Jangan lupa meneruskan radius ke konstruktor class induk.
class RoundTower(
    residents: Int,
    radius: Double,
    val floors: Int = 2) : RoundHut(residents, radius) {
  1. Perbarui inisialisasi roundTower di main().
val roundTower = RoundTower(4, 15.5)
  1. Dan tambahkan pernyataan cetak yang memanggil floorArea().
println("Floor area: ${floorArea()}")
  1. Sekarang Anda sudah dapat menjalankan kode!
  2. Perhatikan bahwa penghitungan untuk RoundTower tidak benar karena diwarisi dari RoundHut dan tidak memperhitungkan jumlah floors.
  3. Di dalam RoundTower, lakukan override floorArea() agar dapat memberikan penerapan berbeda yang mengalikan luas dengan jumlah lantai. Perhatikan bahwa Anda dapat menentukan fungsi dalam class abstrak (Dwelling), menerapkannya dalam subclass (RoundHut), lalu menggantinya lagi dalam subclass dari subclass (RoundTower). Ini adalah cara terbaik untuk keduanya karena Anda mewarisi fungsi yang diinginkan dan dapat mengganti fungsi yang tidak diinginkan.
override fun floorArea(): Double {
    return PI * radius * radius * floors
}

Kode ini berfungsi, tetapi ada cara untuk menghindari pengulangan kode yang sudah ada di class induk RoundHut. Anda dapat memanggil fungsi floorArea() dari class RoundHut induk, yang menampilkan PI * radius * radius. Lalu, kalikan hasil tersebut dengan jumlah floors.

  1. Dalam RoundTower, perbarui floorArea() untuk menggunakan implementasi superclass floorArea(). Gunakan kata kunci super untuk memanggil fungsi yang ditentukan di induk.
override fun floorArea(): Double {
    return super.floorArea() * floors
}
  1. Jalankan lagi kode Anda dan RoundTower menghasilkan ruang lantai yang benar untuk beberapa lantai.

Berikut adalah kode yang sudah selesai:

import kotlin.math.PI

fun main() {

    val squareCabin = SquareCabin(6, 50.0)
    val roundHut = RoundHut(3, 10.0)
    val roundTower = RoundTower(4, 15.5)

    with(squareCabin) {
        println("\nSquare Cabin\n============")
        println("Capacity: ${capacity}")
        println("Material: ${buildingMaterial}")
        println("Has room? ${hasRoom()}")
        println("Floor area: ${floorArea()}")
    }

    with(roundHut) {
        println("\nRound Hut\n=========")
        println("Material: ${buildingMaterial}")
        println("Capacity: ${capacity}")
        println("Has room? ${hasRoom()}")
        println("Floor area: ${floorArea()}")
    }

    with(roundTower) {
        println("\nRound Tower\n==========")
        println("Material: ${buildingMaterial}")
        println("Capacity: ${capacity}")
        println("Has room? ${hasRoom()}")
        println("Floor area: ${floorArea()}")
    }
 }

abstract class Dwelling(private var residents: Int) {

    abstract val buildingMaterial: String
    abstract val capacity: Int

    fun hasRoom(): Boolean {
        return residents < capacity
}

    abstract fun floorArea(): Double
}

class SquareCabin(residents: Int,
    val length: Double) : Dwelling(residents) {

    override val buildingMaterial = "Wood"
    override val capacity = 6

    override fun floorArea(): Double {
       return length * length
    }
}

open class RoundHut(residents: Int,
    val radius: Double) : Dwelling(residents) {

    override val buildingMaterial = "Straw"
    override val capacity = 4

    override fun floorArea(): Double {
        return PI * radius * radius
    }
}

class RoundTower(residents: Int, radius: Double,
    val floors: Int = 2) : RoundHut(residents, radius) {

    override val buildingMaterial = "Stone"
    override val capacity = 4 * floors

    override fun floorArea(): Double {
        return super.floorArea() * floors
    }
}

Output harus berupa:

Square Cabin
============
Capacity: 6
Material: Wood
Has room? false
Floor area: 2500.0

Round Hut
=========
Material: Straw
Capacity: 4
Has room? true
Floor area: 314.1592653589793

Round Tower
==========
Material: Stone
Capacity: 8
Has room? true
Floor area: 1509.5352700498956

Memungkinkan penghuni baru mendapatkan ruangan

Tambahkan kemampuan bagi penghuni baru agar mendapatkan ruangan dengan fungsi getRoom() yang akan meningkatkan jumlah penghuni sebanyak satu. Logika ini sama untuk semua tempat tinggal sehingga Anda dapat mengimplementasikan fungsi di Dwelling, dan logika akan tersedia untuk semua subclass dan turunannya. Keren!

Catatan:

  • Gunakan pernyataan if yang hanya akan menambahkan satu penghuni jika kapasitas tersisa.
  • Cetak pesan untuk mendapatkan hasilnya.
  • Anda dapat menggunakan residents++ sebagai singkatan residents = residents + 1 untuk menambahkan 1 ke variabel residents.
  1. Implementasikan fungsi getRoom() di class Dwelling.
fun getRoom() {
    if (capacity > residents) {
        residents++
        println("You got a room!")
    } else {
        println("Sorry, at capacity and no rooms left.")
    }
}
  1. Tambahkan beberapa pernyataan cetak ke blok pernyataan with untuk roundHut guna mengamati proses yang akan terjadi jika getRoom() dan hasRoom() digunakan bersamaan.
println("Has room? ${hasRoom()}")
getRoom()
println("Has room? ${hasRoom()}")
getRoom()

Output untuk pernyataan cetak ini:

Has room? true
You got a room!
Has room? false
Sorry, at capacity and no rooms left.

Lihat Kode solusi untuk mengetahui detailnya.

Memasang karpet ke dalam tempat tinggal bundar

Misalnya Anda perlu mengetahui panjang satu sisi karpet yang harus digunakan untuk RoundHut atau RoundTower. Masukkan fungsi ke dalam RoundHut untuk menyediakannya ke semua tempat tinggal bundar.

2e328a198a82c793.png

  1. Impor terlebih dahulu fungsi sqrt() dari library kotlin.math.
import kotlin.math.sqrt
  1. Implementasikan fungsi calculateMaxCarpetLength() di class RoundHut. Formula untuk menghitung panjang karpet persegi yang dapat pas dalam lingkaran adalah sqrt(2) * radius. Hal ini dijelaskan dalam diagram di atas.
fun calculateMaxCarpetLength(): Double {

    return sqrt(2.0) * radius
}

Teruskan nilai Double, 2.0 ke fungsi matematika sqrt(2.0), karena jenis nilai fungsi yang ditampilkan adalah Double, bukan Integer.

  1. Sekarang metode calculateMaxCarpetLength() dapat dipanggil pada instance RoundHut dan RoundTower. Tambahkan pernyataan cetak ke roundHut dan roundTower dalam fungsi main().
println("Carpet Length: ${calculateMaxCarpetLength()}")

Lihat Kode solusi untuk mengetahui detailnya.

Selamat! Anda telah membuat hierarki class lengkap dengan properti dan fungsi, serta mempelajari semua yang Anda butuhkan untuk membuat class yang lebih bermanfaat.

6. Kode solusi

Kode solusi lengkap di bawah untuk codelab ini, termasuk komentar.

/**
* Program that implements classes for different kinds of dwellings.
* Shows how to:
* Create class hierarchy, variables and functions with inheritance,
* abstract class, overriding, and private vs. public variables.
*/

import kotlin.math.PI
import kotlin.math.sqrt

fun main() {
   val squareCabin = SquareCabin(6, 50.0)
   val roundHut = RoundHut(3, 10.0)
   val roundTower = RoundTower(4, 15.5)

   with(squareCabin) {
       println("\nSquare Cabin\n============")
       println("Capacity: ${capacity}")
       println("Material: ${buildingMaterial}")
       println("Floor area: ${floorArea()}")
   }

   with(roundHut) {
       println("\nRound Hut\n=========")
       println("Material: ${buildingMaterial}")
       println("Capacity: ${capacity}")
       println("Floor area: ${floorArea()}")
       println("Has room? ${hasRoom()}")
       getRoom()
       println("Has room? ${hasRoom()}")
       getRoom()
       println("Carpet size: ${calculateMaxCarpetLength()}")
   }

   with(roundTower) {
       println("\nRound Tower\n==========")
       println("Material: ${buildingMaterial}")
       println("Capacity: ${capacity}")
       println("Floor area: ${floorArea()}")
       println("Carpet Length: ${calculateMaxCarpetLength()}")
   }
}

/**
* Defines properties common to all dwellings.
* All dwellings have floorspace,
* but its calculation is specific to the subclass.
* Checking and getting a room are implemented here
* because they are the same for all Dwelling subclasses.
*
* @param residents Current number of residents
*/
abstract class Dwelling(private var residents: Int) {
   abstract val buildingMaterial: String
   abstract val capacity: Int

   /**
    * Calculates the floor area of the dwelling.
    * Implemented by subclasses where shape is determined.
    *
    * @return floor area
    */
   abstract fun floorArea(): Double

   /**
    * Checks whether there is room for another resident.
    *
    * @return true if room available, false otherwise
    */
   fun hasRoom(): Boolean {
       return residents < capacity
   }

   /**
    * Compares the capacity to the number of residents and
    * if capacity is larger than number of residents,
    * add resident by increasing the number of residents.
    * Print the result.
    */
   fun getRoom() {
       if (capacity > residents) {
           residents++
           println("You got a room!")
       } else {
           println("Sorry, at capacity and no rooms left.")
       }
   }

   }

/**
* A square cabin dwelling.
*
*  @param residents Current number of residents
*  @param length Length
*/
class SquareCabin(residents: Int, val length: Double) : Dwelling(residents) {
   override val buildingMaterial = "Wood"
   override val capacity = 6

   /**
    * Calculates floor area for a square dwelling.
    *
    * @return floor area
    */
   override fun floorArea(): Double {
       return length * length
   }

}

/**
* Dwelling with a circular floorspace
*
* @param residents Current number of residents
* @param radius Radius
*/
open class RoundHut(
       residents: Int, val radius: Double) : Dwelling(residents) {

   override val buildingMaterial = "Straw"
   override val capacity = 4

   /**
    * Calculates floor area for a round dwelling.
    *
    * @return floor area
    */
   override fun floorArea(): Double {
       return PI * radius * radius
   }

   /**
    *  Calculates the max length for a square carpet
    *  that fits the circular floor.
    *
    * @return length of square carpet
    */
    fun calculateMaxCarpetLength(): Double {
        return sqrt(2.0) * radius
    }

}

/**
* Round tower with multiple stories.
*
* @param residents Current number of residents
* @param radius Radius
* @param floors Number of stories
*/
class RoundTower(
       residents: Int,
       radius: Double,
       val floors: Int = 2) : RoundHut(residents, radius) {

   override val buildingMaterial = "Stone"

   // Capacity depends on the number of floors.
   override val capacity = floors * 4

   /**
    * Calculates the total floor area for a tower dwelling
    * with multiple stories.
    *
    * @return floor area
    */
   override fun floorArea(): Double {
       return super.floorArea() * floors
   }
}

7. Ringkasan

Dalam codelab ini, Anda telah mempelajari cara:

  • Membuat hierarki class, yaitu pohon class tempat turunan mewarisi fungsi dari class induk. Properti dan fungsi diwarisi oleh subclass.
  • Membuat class abstract tempat beberapa fungsi dibiarkan untuk diimplementasikan oleh subclass-nya. Oleh karena itu, instance untuk class abstract tidak dapat dibuat.
  • Membuat subclass dari class abstract.
  • Menggunakan kata kunci override untuk mengganti properti dan fungsi di subclass.
  • Menggunakan kata kunci super untuk mereferensikan fungsi dan properti di class induk.
  • Membuat class open agar dapat dibuatkan subclass-nya.
  • Membuat properti private agar hanya dapat digunakan di dalam class.
  • Menggunakan konstruksi with untuk melakukan beberapa panggilan pada instance objek yang sama.
  • Mengimpor fungsi dari library kotlin.math

8. Mempelajari lebih lanjut