Pewarisan (inheritance) memungkinkan satu kelas mewarisi properti dan metode kelas lain, sehingga perilaku yang sama ditulis sekali di kelas induk dan dipakai ulang oleh seluruh kelas turunannya. Topik ini membahas kata kunci extends, pemanggilan anggota induk melalui parent::, penimpaan (override) metode, serta kelas abstrak yang menunda sebagian implementasi kepada kelas turunannya. Contohnya masih mengikuti domain perpustakaan, yaitu pengguna, anggota, dan item koleksi dengan durasi peminjaman yang berbeda.
Mewarisi kelas dengan extends#
Kelas turunan dibuat dengan kata kunci extends pada deklarasinya. Relasi ini menyatakan bahwa kelas turunan adalah jenis dari kelas induknya, misalnya anggota adalah pengguna. Kelas turunan memperoleh seluruh anggota induk yang tidak dibuat private, kemudian menambahkan anggota baru atau memperbaiki perilaku yang diwarisi. Pada PHP, satu kelas hanya boleh mewarisi satu kelas induk, sehingga jalur pewarisannya selalu berupa satu garis lurus ke atas.
Constructor induk tidak dipanggil secara otomatis apabila kelas turunan mendefinisikan constructor sendiri, sehingga pemanggilan parent::__construct(...) perlu ditulis secara eksplisit di dalamnya. Kelalaian ini lazim karena objek tetap berhasil dibuat, tetapi properti milik induk tidak pernah terisi dan baru bermasalah ketika dibaca.
Skrip berikut mendefinisikan kelas Pengguna beserta kelas Anggota yang mewarisinya. Constructor Anggota meneruskan nama ke constructor induknya, menambahkan properti nomor kartu, dan memakai metode sapa() milik induk tanpa menulis ulangnya.
<?php
declare(strict_types=1);
class Pengguna
{
public function __construct(protected string $nama)
{
}
public function sapa(): string
{
return "Halo, {$this->nama}.";
}
}
class Anggota extends Pengguna
{
public function __construct(
string $nama,
private string $nomorKartu,
) {
parent::__construct($nama);
}
public function kartu(): string
{
return "Kartu {$this->nomorKartu} atas nama {$this->nama}.";
}
}
$anggota = new Anggota('Dewi', 'KRT-007');
echo $anggota->sapa(), "\n";
echo $anggota->kartu(), "\n";
var_dump($anggota instanceof Pengguna);Halo, Dewi.
Kartu KRT-007 atas nama Dewi.
bool(true)Baris pertama keluaran berasal dari metode milik Pengguna, sedangkan baris kedua berasal dari metode milik Anggota yang membaca properti dari kedua kelas. Pemeriksaan instanceof pada baris terakhir menegaskan bahwa setiap objek Anggota juga dihitung sebagai objek Pengguna, dan itulah arti praktis dari hubungan pewarisan.
Menimpa metode dan parent::#
Kelas turunan boleh menimpa metode induknya dengan mendefinisikan metode bernama sama beserta tanda tangan yang kompatibel. PHP selalu menjalankan versi milik kelas objek yang sedang dipakai, sehingga metode induk yang memanggil $this->metode() otomatis menjalankan versi penimpanya. Aturan yang menyertainya adalah visibilitas metode penimpa tidak boleh lebih ketat daripada versi induknya, misalnya metode public tidak boleh ditimpa sebagai private.
Penimpaan sering bukan penggantian total, melainkan perluasan atas hasil induknya. Untuk keperluan itu, parent::metode() memanggil versi induknya secara langsung dari dalam versi penimpanya, sehingga hasil induk dapat dipakai lalu diberi tambahan. Pola ini juga berlaku pada constructor yang sudah diperlihatkan sebelumnya.
Skrip berikut menetapkan durasi peminjaman bawaan tujuh hari pada kelas Item, lalu menimpanya menjadi empat belas hari pada Buku dan tiga hari pada Majalah. Kelas Majalah sekaligus menimpa deskripsi() dengan memanggil parent::deskripsi() lalu menambahkan keterangannya.
<?php
declare(strict_types=1);
class Item
{
public function __construct(protected string $judul)
{
}
public function lamaPinjam(): int
{
return 7;
}
public function deskripsi(): string
{
return "{$this->judul}, lama pinjam {$this->lamaPinjam()} hari";
}
}
class Buku extends Item
{
public function lamaPinjam(): int
{
return 14;
}
}
class Majalah extends Item
{
public function lamaPinjam(): int
{
return 3;
}
public function deskripsi(): string
{
return parent::deskripsi() . ', edisi mingguan';
}
}
$item = new Item('Panduan Singkat');
$buku = new Buku('Algoritma Dasar');
$majalah = new Majalah('Jurnal Kampus');
echo $item->deskripsi(), "\n";
echo $buku->deskripsi(), "\n";
echo $majalah->deskripsi(), "\n";Panduan Singkat, lama pinjam 7 hari
Algoritma Dasar, lama pinjam 14 hari
Jurnal Kampus, lama pinjam 3 hari, edisi mingguanPerhatikan bahwa deskripsi() hanya ditulis sekali pada kelas Item, tetapi angka durasinya mengikuti kelas masing-masing karena $this->lamaPinjam() diselesaikan pada saat pemanggilan. Baris ketiga membuktikan bahwa versi penimpa dapat menyusun ulang hasil induknya, yaitu memanggil parent::deskripsi() terlebih dahulu lalu menambahkan keterangan edisi.
Kelas abstrak#
Kelas abstrak adalah kelas yang diberi kata kunci abstract dan tidak dapat dijadikan objek secara langsung. Kelas semacam ini berperan sebagai induk yang menampung keadaan dan perilaku bersama, sementara satu atau beberapa metodenya dinyatakan abstract, yaitu hanya deklarasi tanda tangan tanpa tubuh. Setiap kelas turunan yang tidak abstrak wajib menyediakan implementasi seluruh metode abstrak tersebut dengan tanda tangan yang kompatibel.
Kelas abstrak tetap boleh memuat properti, constructor, dan metode konkret, sehingga kode yang sama tidak perlu ditulis di setiap turunan. Perbedaannya dengan kelas biasa terletak pada kontrak yang ditegakkan, yaitu kompilasi gagal apabila ada turunan yang melupakan satu metode abstrak pun. Dengan demikian, kelas abstrak cocok dipakai ketika sebagian perilaku dibagi bersama dan sebagian lagi harus berbeda pada setiap jenis turunan.
Skrip berikut menjadikan ItemKoleksi kelas abstrak dengan metode konkret deskripsi() dan metode abstrak durasiPinjam(). Dua turunannya menyediakan angka yang berbeda, dan percobaan membuat objek langsung dari kelas abstrak ditangkap untuk menunjukkan pesan errornya.
<?php
declare(strict_types=1);
abstract class ItemKoleksi
{
public function __construct(protected string $judul)
{
}
abstract public function durasiPinjam(): int;
public function deskripsi(): string
{
return "{$this->judul}, pinjam {$this->durasiPinjam()} hari";
}
}
class Buku extends ItemKoleksi
{
public function durasiPinjam(): int
{
return 14;
}
}
class Majalah extends ItemKoleksi
{
public function durasiPinjam(): int
{
return 3;
}
}
$buku = new Buku('Algoritma Dasar');
$majalah = new Majalah('Jurnal Kampus');
echo $buku->deskripsi(), "\n";
echo $majalah->deskripsi(), "\n";
try {
new ItemKoleksi('Contoh');
} catch (Throwable $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}Algoritma Dasar, pinjam 14 hari
Jurnal Kampus, pinjam 3 hari
Error: Cannot instantiate abstract class ItemKoleksiBaris error terakhir menegaskan sifat kelas abstrak sebagai templat, yaitu objek hanya dapat dibuat dari kelas yang seluruh metode abstraknya sudah diimplementasikan. Pemilihan antara kelas abstrak dan interface mengikuti kebutuhan. Kelas abstrak membawa implementasi bersama dan keadaan, sedangkan interface hanya menetapkan kontrak tanpa keadaan. Perbandingan lengkapnya dibahas pada topik interface.
Kesalahan umum#
- Lupa memanggil
parent::__construct(). Constructor turunan yang tidak memanggil constructor induk membuat properti milik induk tidak pernah terinisialisasi, dan pembacaannya memicu error properti bertipe belum terisi. Pastikan setiap constructor turunan memanggil constructor induknya sebelum menambahkan inisialisasi sendiri. - Menimpa metode dengan visibilitas yang lebih ketat. Metode
publicmilik induk yang ditimpa sebagaiprotectedatauprivatemenggagalkan pemuatan kelas karena aturan pewarisan melarang pengetatan. Pertahankan visibilitas yang sama, atau longgarkan bila memang diperlukan. - Mengubah tanda tangan yang tidak kompatibel saat menimpa. Mengganti tipe parameter atau tipe kembalian secara tidak kompatibel memicu error deklarasi. Ikuti tanda tangan induknya, dan gunakan tipe yang lebih umum pada parameter bila memang diperlukan.
- Menjadikan pewarisan sebagai alat berbagi kode semata. Relasi pewarisan yang tidak nyata, misalnya kelas
Laporanmewarisi kelasBukuhanya karena membutuhkan judulnya, membuat hierarki menjadi sulit diubah. Pertimbangkan komposisi, yaitu memegang objek lain sebagai properti, apabila hubungannya bukan pewarisan yang sejati.
Ringkasan#
extendsmewariskan anggota induk kepada kelas turunan, sehingga objek turunan juga dikenali sebagai objek induk olehinstanceof.- Constructor induk harus dipanggil eksplisit melalui
parent::__construct(), sedangkan metode induk yang ditimpa dapat dipanggil kembali melaluiparent::. - Metode yang ditimpa harus mempertahankan tanda tangan yang kompatibel dan visibilitas yang tidak lebih ketat, dan PHP menjalankan versi milik kelas objek yang sedang dipakai.
- Kelas abstrak tidak dapat dijadikan objek, menampung implementasi bersama, dan mewajibkan setiap turunan konkretnya melengkapi seluruh metode abstraknya.
Referensi#
- Object Inheritance — Manual PHP
— Menetapkan aturan
extends, penimpaan metode, dan kompatibilitas tanda tangan. - Class Abstraction — Manual PHP — Menjelaskan kelas abstrak, metode abstrak, dan kewajiban implementasinya.
- Constructors and Destructors — Manual PHP
— Menjelaskan pemanggilan constructor induk melalui
parent::__construct().