Di OOP Dasar kamu bikin satu class lalu beberapa object darinya. Di lesson ini class-class mulai berhubungan: satu class bisa mewarisi isi class lain (inheritance), sebuah class bisa sengaja dibuat belum lengkap supaya anaknya yang melengkapi (abstract class), dan sebuah class bisa berjanji punya method tertentu (interface). Bayangin buku menu Warung Bu Tini. Tiap halaman dicetak dari template yang sama: tempat nama dan harga sudah ada. Kolom "kategori" sengaja dikosongkan di template, tiap halaman wajib mengisinya sendiri, dan template kosong itu gak pernah dijual. Beberapa halaman ditempeli stiker "Bisa Diantar", tanda halaman itu punya tarif ongkir. Template adalah class induk, kolom kosong adalah method abstract, stikernya interface.
🧪 Tebakan cepat:
Menuadalah abstract class. Apa yang terjadi kalau kamu menulisnew Menu("Nasi Goreng", 15000)? Object-nya jadi tapi kosong, atau PHP menolak? Tebak dulu, jawabannya ada di bagian "Abstract class: induk yang sengaja belum lengkap".
Inheritance: extends mewarisi isi induk
<?php
class Menu {
public function __construct(public string $nama, public int $harga) {}
public function info(): string {
return "$this->nama - Rp$this->harga";
}
}
class Minuman extends Menu {
public function info(): string {
return parent::info() . " (pakai es)";
}
}
$teh = new Minuman("Es Teh", 5000);
echo $teh->info(); // Es Teh - Rp5000 (pakai es)
class Minuman extends Menu: Minuman mewarisi semua isi Menu, yaitu$nama,$harga, constructor, daninfo(). Menu disebut class induk (parent), Minuman class anak (child).new Minuman("Es Teh", 5000)memakai constructor milik Menu, karena Minuman gak menulis constructor sendiri.- Minuman menulis ulang
info()dengan nama yang sama. Ini override: untuk object Minuman, versi anaknya yang dipakai. parent::info()menjalankan versi induk dulu, lalu anaknya menambahkan teks. Isi method induk gak perlu disalin.
Satu class cuma boleh extends satu induk. Siapa yang boleh menyentuh sebuah property atau method diatur visibility-nya:
| Kata kunci | Bisa dipakai dari | Kalau dilanggar dari luar class |
|---|---|---|
public |
mana saja | |
protected |
class itu sendiri dan anak-anaknya | Cannot access protected property Menu::$harga |
private |
class itu sendiri saja, anaknya pun gak bisa | Cannot access private property Menu::$stok |
Abstract class: induk yang sengaja belum lengkap
<?php
abstract class Menu {
public function __construct(public string $nama, public int $harga) {}
abstract public function kategori(): string;
public function info(): string {
return $this->kategori() . ": $this->nama - Rp$this->harga";
}
}
class Makanan extends Menu {
public function kategori(): string {
return "Makanan";
}
}
$nasi = new Makanan("Nasi Goreng", 15000);
echo $nasi->info() . "\n";
$menu = new Menu("Nasi Goreng", 15000);
echo "baris ini gak pernah jalan";
$ php menu.php
Makanan: Nasi Goreng - Rp15000
Fatal error: Uncaught Error: Cannot instantiate abstract class Menu in /var/www/warung/menu.php:21
Stack trace:
#0 {main}
thrown in /var/www/warung/menu.php on line 21
abstract class Menu: Menu cuma cetakan induk. Instantiate artinya membuat object dengannew, dan PHP menolak melakukannya untuk class abstract. Object-nya gak pernah jadi dan script berhenti di baris 21. Itu jawaban tebakannya.abstract public function kategori(): string;adalah method tanpa isi, diakhiri titik koma. Artinya "tiap anak wajib menulis method ini".info()di induk sudah memanggil$this->kategori()walau isinya belum ada. Aman, karena object yang ada selalu object anak yang sudah melengkapinya.- Anak yang lupa menulis
kategori()ditolak saat class-nya dibaca:Class Makanan contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Menu::kategori).
Latihan lesson ini memakai pola yang sama: abstract class Bangun dengan luas() abstract, lalu Lingkaran dan Segitiga yang masing-masing mengisi rumusnya.
Interface: janji punya method tertentu
interface BisaDiantar {
public function ongkir(int $jarakKm): int;
}
class Makanan extends Menu implements BisaDiantar {
public function kategori(): string { return "Makanan"; }
public function ongkir(int $jarakKm): int { return 2000 * $jarakKm; }
}
class Minuman extends Menu implements BisaDiantar {
public function kategori(): string { return "Minuman"; }
}
$ php menu.php
Fatal error: Class Minuman contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (BisaDiantar::ongkir) in /var/www/warung/menu.php on line 21
interfacecuma berisi daftar method: nama, parameter, dan tipe return-nya, tanpa isi. Interface gak bisa di-new.implements BisaDiantaradalah janji. PHP menagih janjinya saat class dibaca, sebelum object apa pun dibuat. Minuman belum punyaongkir(), jadi PHP berhenti di barisclass Minuman(baris 21 di file lengkapnya, setelah classMenudari bagian sebelumnya).- Tambahkan
ongkir()di Minuman, misalnyareturn 1000 * $jarakKm;, dan error-nya hilang. Es Teh 3 km jadi Rp3000, Nasi Goreng 3 km Rp6000. - Satu class boleh
implementsbanyak interface sekaligus (implements BisaDiantar, Countable), tapi tetap cuma satuextends. $menu instanceof BisaDiantarmenjawabtruekalau object itu menepati janji tersebut, berguna sebelum memanggilongkir().
| Abstract class | Interface | |
|---|---|---|
| Isinya | property, constructor, method lengkap, method abstract | daftar method tanpa isi |
| Cara pakai | extends, satu induk |
implements, boleh banyak |
| Pas untuk | anak-anak yang berbagi data dan kode | class yang gak bersaudara tapi butuh kemampuan yang sama |
💡 final, lalu self:: lawan static::
final mengunci sesuatu supaya gak bisa diubah anaknya:
abstract class Menu {
// ...
final public function info(): string {
return "$this->nama - Rp$this->harga";
}
}
class Promo extends Menu {
public function info(): string { return "GRATIS"; }
}
// Fatal error: Cannot override final method Menu::info()
final class Kasir {} mengunci class-nya: class KasirCurang extends Kasir {} ditolak dengan Class KasirCurang cannot extend final class Kasir.
Method static dipanggil lewat nama class dan ::, tanpa object. Di dalamnya, self dan static sama-sama menunjuk class, tapi beda class:
abstract class Menu {
// ...
public static function buat(string $nama, int $harga): static {
return new static($nama, $harga);
}
}
$nasi = Makanan::buat("Nasi Goreng", 15000);
echo get_class($nasi); // Makanan
new staticmembuat object dari class yang dipanggil, di sini Makanan. Namanya late static binding: class-nya baru ditentukan saat dipanggil.new selfmembuat object dari class tempat kodenya ditulis, yaitu Menu. Karena Menu abstract,Makanan::buat(...)berakhir denganCannot instantiate abstract class Menu.- Sama untuk nama class: dari
Makanan::...,static::classberisiMakanandanself::classberisiMenu.
💡 Enum dengan method
Enum adalah tipe dengan daftar nilai yang tertutup. Ukuran gelas Es Teh cuma S atau L, gak boleh yang lain:
enum Ukuran: string {
case Kecil = "S";
case Besar = "L";
public function tambahan(): int {
return match ($this) {
Ukuran::Kecil => 0,
Ukuran::Besar => 3000,
};
}
}
$u = Ukuran::from("L");
echo $u->name; // Besar
echo $u->value; // L
echo 5000 + $u->tambahan(); // 8000
var_dump(Ukuran::tryFrom("XL")); // NULL
Ukuran::from("XL"); // ValueError: "XL" is not a valid backing value for enum Ukuran
case Kecil = "S";adalah satu nilai enum.: stringmembuatnya backed enum: tiap case membawa nilai string.->namenama case-nya,->valuenilai string-nya,Ukuran::cases()daftar semua case (2 di sini).from()mengubah teks jadi case dan melemparValueErrorkalau gak ada yang cocok.tryFrom()mengembalikannullsaja.- Enum boleh punya method seperti class.
match ($this)memilih hasil sesuai case-nya.
⚠️ Jebakan yang sering ketemu
newpada abstract class.new Menu(...)ataunew Bangun()selalu ditolak denganCannot instantiate abstract class. Yang dibuat object-nya class anaknya.- Lupa menulis method yang diwajibkan. Method abstract di induk dan method di interface wajib ditulis di anaknya, dengan nama dan tipe return yang sama. Kalau gak,
contains 1 abstract method. privatedikira ikut terpakai di anak. Anak gak bisa membaca propertyprivatemilik induknya. Pakaiprotectedkalau anaknya butuh.- Mengetik nilai pi sendiri.
3.14 * 7 * 7hasilnya 153,86, bukan 153,94. Pakai konstanta bawaanM_PI, laluround($x, 2)membulatkannya jadi153.94.
TL;DR: extends membuat class anak mewarisi isi induknya (satu induk saja), method yang ditulis ulang di anak meng-override induk, dan parent:: memanggil versi induk. public bisa dari mana saja, protected dari class itu dan anaknya, private hanya dari class itu sendiri. abstract class gak bisa di-new (Cannot instantiate abstract class) dan method abstract-nya wajib diisi anaknya. interface adalah janji daftar method, dipasang dengan implements (boleh banyak), dan PHP menagihnya saat class dibaca. final mengunci method atau class, new static membuat object dari class yang dipanggil sedangkan new self dari class tempat kodenya ditulis, dan enum adalah daftar nilai tertutup yang boleh punya method. Untuk luas lingkaran pakai M_PI.