Kelas MustInherit dan NotInheritable

Kelas abstrak dengan MustInherit/MustOverride dan kelas terkunci dengan NotInheritable.

Daftar isi

Kata kunci MustInherit dan NotInheritable mengatur dua ujung dari perluasan kelas, yaitu kelas yang wajib diwarisi sebelum digunakan dan kelas yang dilarang diwarisi sama sekali. Topik ini membahas kelas abstrak dengan MustInherit beserta anggota MustOverride-nya, kelas terkunci dengan NotInheritable, serta NotOverridable untuk menutup rantai penimpaan. Contoh yang dijalankan membangun kelas abstrak PembacaSensor dengan dua turunannya, dan pesan error compiler yang menolak pembuatan objek dari kelas abstrak ditampilkan dari eksekusi nyata.

Kelas abstrak dengan MustInherit#

Kelas yang diberi penanda MustInherit disebut kelas abstrak, yaitu kelas yang hanya dapat digunakan sebagai kelas induk dan tidak dapat diinstansiasi secara langsung. Operator New pada kelas semacam ini ditolak compiler, sehingga satu-satunya jalan menggunakannya adalah mewarisinya. Kelas abstrak cocok menjadi titik temu ketika beberapa kelas berbagi keadaan dan perilaku yang sama, tetapi satu bagian perilakunya justru harus berbeda di tiap kelas turunannya.

Berbeda dari antarmuka, kelas abstrak boleh memuat field, properti, konstruktor, dan metode yang sudah memiliki badan. Karena itu kelas PembacaSensor berikut dapat menyimpan properti Lokasi dan menyediakan metode Tampilkan yang langsung siap digunakan turunannya, sementara satu metode sengaja dibiarkan tanpa badan untuk diisi turunannya masing-masing.

vb Program.vb
MustInherit Class PembacaSensor

    Public Property Lokasi As String

    ' MustOverride: hanya deklarasi, badannya wajib diisi kelas turunan
    Public MustOverride Function BacaNilai() As Double

    ' metode biasa tetap boleh ada dan menggunakan hasil MustOverride
    Public Sub Tampilkan()
        Console.WriteLine($"{Lokasi}: {BacaNilai()}")
    End Sub

End Class

Class PembacaSuhu
    Inherits PembacaSensor

    Public Property SuhuCelsius As Double

    Public Overrides Function BacaNilai() As Double
        Return SuhuCelsius
    End Function

End Class

Class PembacaKelembapan
    Inherits PembacaSensor

    Public Property PersenKelembapan As Double

    Public Overrides Function BacaNilai() As Double
        Return PersenKelembapan
    End Function

End Class

Module Program

    Sub Main()
        Dim suhu As New PembacaSuhu With {.Lokasi = "Ruang server", .SuhuCelsius = 24.5}
        Dim lembap As New PembacaKelembapan With {.Lokasi = "Gudang arsip", .PersenKelembapan = 68}

        suhu.Tampilkan()
        lembap.Tampilkan()
    End Sub

End Module
text Keluaran
Ruang server: 24.5
Gudang arsip: 68

Pemanggilan Tampilkan dilakukan pada objek PembacaSuhu dan PembacaKelembapan, sedangkan tidak ada satu pun objek PembacaSensor yang dibuat. Metode Tampilkan yang badannya ada di kelas abstrak tetap memanggil BacaNilai(), dan pemanggilan itu mendarat pada versi milik kelas objeknya, yaitu SuhuCelsius untuk sensor suhu dan PersenKelembapan untuk sensor kelembapan. Pola ini memungkinkan kelas abstrak menulis alur bersama sekali, sementara bagian yang berbeda diserahkan kepada turunannya.

Upaya membuat objek langsung dari kelas abstrak ditolak pada waktu kompilasi, seperti tampak pada fragment dan transkrip berikut.

vb Contoh: New pada kelas MustInherit ditolak compiler
' tidak-dijalankan
Dim pembaca As New PembacaSensor() ' error BC30569: New tidak boleh dipakai pada kelas yang dinyatakan MustInherit
text Transkrip terminal
$ bash /home/kal/my-project/scripts/vb-build.sh /home/kal/my-project/build-scratch/oop__uji-tolak2
/home/kal/my-project/build-scratch/oop__uji-tolak2/Program.vb(14,18): error BC30569: 'New' cannot be used on a class that is declared 'MustInherit'. [/home/kal/my-project/build-scratch/oop__uji-tolak2/WinForms.vbproj]

Build FAILED.

MustOverride dan kontrak yang wajib diisi#

Anggota yang diberi penanda MustOverride adalah deklarasi kepala prosedur tanpa badan, tanpa End Function, dan wajib berada di dalam kelas yang juga MustInherit. Kelas turunan wajib menimpanya dengan Overrides, dan compiler menolak kelas turunan yang masih menyisakan anggota MustOverride tanpa implementasi. Dari sudut pandang turunan, anggota ini bekerja seperti kontrak antarmuka yang menyatu dengan pewarisan kelas.

Pembagian kerja pada contoh mengikuti pola itu secara ketat. PembacaSensor menjanjikan bahwa setiap turunannya dapat dibaca melalui BacaNilai() As Double, tetapi tidak mengatur dari mana angkanya berasal. Kelas PembacaSuhu mengisinya dari properti SuhuCelsius, sedangkan kelas PembacaKelembapan mengisinya dari properti PersenKelembapan, dan penambahan jenis sensor baru di masa depan cukup mengisi kontrak yang sama. Semua turunan otomatis mendapatkan metode Tampilkan tanpa menyentuhnya lagi, sehingga aturan tampilan tidak pernah melebar ke luar kelas abstraknya.

Kelas terkunci dengan NotInheritable#

Kelas yang diberi penanda NotInheritable berada di ujung yang berlawanan, yaitu kelas siap pakai yang dilarang diwarisi. Operator New terhadapnya sah dan program dapat membuat objeknya, sedangkan pernyataan Inherits terhadap kelasnya ditolak compiler dengan pesan error BC30299. Penanda ini padanan dari konsep sealed pada bahasa .NET lainnya, dan kelas pustaka standar yang perilakunya memang final umumnya dideklarasikan dengan cara ini.

vb Contoh: pewarisan atas kelas NotInheritable ditolak compiler
' tidak-dijalankan
NotInheritable Class Utilitas
End Class

Class TurunanUtilitas
    Inherits Utilitas  ' error BC30299: kelas tidak boleh mewarisi kelas yang dinyatakan NotInheritable
End Class

Alasan mengunci kelas biasanya berkaitan dengan jaminan perilaku. Kelas yang memuat aturan keamanan, fungsi matematika, atau utilitas kecil yang sudah selesai tidak memerlukan titik perluasan, sehingga melarang pewarisan mencegah turunan mengubah jaminan itu tanpa disengaja. Penanda NotOverridable menutup hal yang sama pada tingkat anggota, yaitu metode Overrides yang diberi NotOverridable tidak dapat ditimpa lagi oleh turunan berikutnya. Anggota kelas biasa tanpa Overridable sebenarnya sudah tertutup sejak awal, sehingga NotOverridable hanya relevan pada rantai penimpaan yang sedang berlangsung.

Memilih antara kelas abstrak dan kelas terkunci#

Keputusan antara MustInherit dan NotInheritable mengikuti rencana perluasan kelas. Kelas yang menyediakan alur kerja bersama tetapi menyisakan langkah yang harus berbeda per turunan dinyatakan MustInherit, dan langkah yang diserahkan itu dituliskan sebagai MustOverride. Kelas yang perilakunya sudah lengkap dan tidak dirancang untuk diperluas dinyatakan NotInheritable, sedangkan kelas biasa tanpa kedua penanda tetap dapat diwarisi sekaligus diinstansiasi.

Ketiganya bersanding dengan antarmuka pada perancangan kelas. Antarmuka menjanjikan kemampuan tanpa membagi kode apa pun, kelas abstrak membagi kode bersama sekaligus menuntut pengisian bagian yang kosong, sedangkan kelas terkunci menawarkan implementasi final tanpa janji perluasan. Kombinasi yang lazim adalah antarmuka untuk kontrak publiknya, kelas abstrak sebagai induk berisi kerangka kerjanya, dan implementasi konkret sebagai turunan yang mengisi langkah-langkahnya.

Ukuran praktisnya menyangkut dua pertanyaan, yaitu apakah kelasnya akan diperluas dan apakah sebagian perilakunya memang harus diisi turunannya. Jawaban ya untuk keduanya mengarah pada MustInherit, sedangkan jawaban tidak untuk keduanya mengarah pada NotInheritable. Kelas biasa tanpa penanda tetap menjadi pilihan tengah ketika kelasnya mungkin diperluas tetapi tidak memiliki bagian yang wajib diisi.

Kesalahan umum#

Kesalahan pertama adalah memanggil New pada kelas MustInherit, yang ditolak dengan error BC30569. Perbaikannya bukan menghilangkan penandanya, melainkan membuat objek dari kelas turunannya, karena keberadaan kelas abstrak memang disengaja agar turunannya yang diinstansiasi. Apabila kelasnya ternyata memang perlu dibuat objeknya langsung, penandanya dilepas dan anggota MustOverride-nya diberi badan.

Kesalahan kedua adalah kelas turunan yang belum menimpa seluruh anggota MustOverride induknya. Compiler menolak kelas itu dan menuntut implementasi untuk setiap anggota yang tersisa, atau apabila kelasnya memang tidak mengisinya, kelas turunan itu sendiri harus dinyatakan MustInherit agar tuntutan diteruskan ke turunan berikutnya. Pesan errornya menyebut anggota yang belum diimplementasikan, sehingga baris yang perlu dilengkapi langsung terbaca.

Kesalahan ketiga adalah mencoba mewarisi kelas NotInheritable, yang ditolak dengan error BC30299. Perbaikan yang benar menyesuaikan maksudnya, yaitu menggunakan kelasnya apa adanya melalui New, menyalin kebutuhan kecil ke kode sendiri, atau meminta kelas tersebut dibuka untuk diwarisi apabila memang dirancang ulang oleh pemiliknya. Menambahkan turunan pada kelas yang sudah final bukan perbaikan yang tersedia pada bahasanya.

Ringkasan#

  • Kelas MustInherit adalah kelas abstrak yang tidak dapat diinstansiasi, boleh memuat anggota berbadan, dan menuntut kelas turunannya mengisi anggota MustOverride melalui Overrides.
  • Kelas NotInheritable adalah kelas final yang boleh diinstansiasi tetapi dilarang diwarisi, sedangkan NotOverridable menutup rantai penimpaan pada tingkat anggota.
  • Pemilihan keduanya mengikuti rencana perluasan, yaitu kerangka yang menuntut pengisian dengan MustInherit, implementasi final dengan NotInheritable, dan kontrak murni dengan antarmuka.

Referensi#