Masukkan Password
55 Interface questions in Dart | Dart Interface questions | Dart Tutorial #55
Di video ini sebenarnya pembahasannya lebih dalam daripada video sebelumnya. Fokusnya bukan lagi “apa itu interface?”, tetapi “apa yang terjadi dalam berbagai situasi ketika interface digunakan?”
Saya akan menjelaskan 15 pertanyaan tersebut dengan bahasa sederhana, contoh kode Dart, dan saya juga akan meluruskan beberapa bagian transcript yang sudah agak tua/kurang tepat untuk Dart modern.
1. Kenapa method dari interface harus di-override?
Misalnya kita punya:
class A {
void test() {
print("Test dari A");
}
}
Ada dua cara menggunakan A dari B.
Menggunakan extends
class B extends A {
}
Karena B mewarisi A, method test() otomatis tersedia.
Jadi:
void main() {
B obj = B();
obj.test();
}
Output:
Test dari A
Tidak perlu menulis ulang test().
Menggunakan implements
class B implements A {
}
Sekarang maknanya berbeda.
A tidak menjadi superclass B.
A diperlakukan sebagai interface/contract.
Maka B harus menyediakan implementasi untuk member instance yang diwajibkan oleh interface tersebut.
class B implements A {
@override
void test() {
print("Test dari B");
}
}
Jadi:
extends
↓
warisi implementasi
implements
↓
memenuhi contract
Analogi
Bayangkan A adalah dokumen persyaratan pekerjaan.
Dokumen mengatakan:
“Karyawan harus mempunyai method
test().”
Kalau B menandatangani kontrak tersebut:
class B implements A
maka B harus menyediakan test() sendiri.
2. Bagaimana satu class bisa mengimplementasikan banyak interface?
Ini salah satu keuntungan terbesar implements.
Dart tidak mengizinkan:
class C extends A, B { // ❌
}
Satu class hanya mempunyai satu superclass langsung.
Tetapi kita bisa:
class C implements A, B {
}
Misalnya:
class A {
void test() {
print("A");
}
}
class B {
void demo() {
print("B");
}
}
class C implements A, B {
@override
void test() {
print("Test C");
}
@override
void demo() {
print("Demo C");
}
}
Sekarang C mempunyai dua contract:
A
└── test()
B
└── demo()
↓
C
├── test()
└── demo()
Kenapa tidak ada konflik?
Karena implements tidak mengambil implementasi method dari A dan B.
Yang diwajibkan adalah contract/member-nya.
Jadi:
class A {
void test() {
print("A");
}
}
class B {
void test() {
print("B");
}
}
Kemudian:
class C implements A, B {
@override
void test() {
print("C");
}
}
Ini masih valid.
Kenapa?
Karena kedua interface sama-sama mengatakan:
“C harus punya
test().”
Satu method test() milik C sudah memenuhi keduanya.
Jadi bukan:
A.test()
B.test()
yang harus dibuat di C.
Melainkan cukup:
C.test()
3. Bagaimana kalau dua interface punya nama method sama tetapi parameter berbeda?
Nah, ini bagian yang penting.
Misalnya:
class A {
void test() {}
}
class B {
void test(int x) {}
}
Kemudian:
class C implements A, B {
}
Masalahnya adalah C harus menyediakan:
void test()
dan:
void test(int x)
Dalam bahasa yang memiliki method overloading seperti Java, hal seperti ini bisa dibuat.
Tetapi Dart tidak mendukung method overloading berdasarkan parameter seperti Java/C++.
Jadi kita tidak bisa mengandalkan:
void test() {}
void test(int x) {}
sebagai dua method berbeda dalam class Dart yang sama.
Kenapa?
Karena Dart tidak menggunakan konsep:
“Nama sama + parameter berbeda = method berbeda.”
Dalam Dart, kita biasanya menggunakan:
- optional parameter
- named parameter
- atau nama method berbeda.
Misalnya daripada:
test()
test(int x)
bisa dibuat:
void test([int? x]) {
if (x == null) {
print("Tanpa parameter");
} else {
print("Dengan parameter $x");
}
}
Jadi poin video ini benar sebagai masalah desain di Dart, tetapi penjelasan transcript mengenai “method signature” sebaiknya jangan dianggap sebagai dukungan terhadap overloading Dart.
4. Kenapa constructor interface tidak dipanggil?
Ini sangat penting karena berkaitan dengan perbedaan:
extends
dan:
implements
Misalnya:
class A {
A() {
print("Constructor A");
}
}
class B extends A {
B() {
print("Constructor B");
}
}
Ketika:
B();
output:
Constructor A
Constructor B
Kenapa?
Karena B mewarisi dari A.
Sebelum object B selesai dibuat, bagian A harus diinisialisasi terlebih dahulu.
Bagaimana dengan implements?
class A {
A() {
print("Constructor A");
}
}
class B implements A {
B() {
print("Constructor B");
}
@override
void someMethod() {}
}
Ketika:
B();
yang dipanggil hanya:
Constructor B
Constructor A tidak dipanggil.
Kenapa?
Karena:
class B implements A
tidak berarti:
B adalah turunan A.
Tetapi:
B berjanji mengikuti contract A.
B tidak mewarisi state/implementasi instance dari A.
Jadi tidak ada bagian object A yang perlu diinisialisasi sebagai superclass.
Ingat:
extends A
↓
A adalah superclass
implements A
↓
A adalah interface/contract
5. Bagaimana dengan instance variable di interface?
Misalnya:
class A {
int data = 10;
void test() {}
}
Kemudian:
class B implements A {
}
B harus memenuhi contract instance member dari A.
Artinya B perlu menyediakan data.
Bisa dengan field:
class B implements A {
@override
int data = 20;
@override
void test() {}
}
Atau menggunakan getter/setter:
class B implements A {
int _myData = 20;
@override
int get data => _myData;
@override
set data(int value) {
_myData = value;
}
@override
void test() {}
}
Kenapa getter dan setter juga bisa?
Karena property:
int data
secara konsep memiliki akses baca/tulis.
Ketika interface meminta data, implementasinya bisa berupa:
int data;
atau accessor:
int get data => ...;
set data(int value) => ...;
Jadi yang penting adalah contract-nya terpenuhi.
6. Kenapa static member interface tidak perlu di-override?
Misalnya:
class A {
static int count = 10;
static void showData() {
print(count);
}
}
Kemudian:
class B implements A {
}
B tidak diwajibkan membuat:
static int count;
atau:
static void showData();
Kenapa?
Karena static bukan milik object.
Bedakan instance dan static
Instance:
class A {
int x = 10;
}
Setiap object punya x sendiri.
A object 1 → x = 10
A object 2 → x = 10
Sedangkan:
class A {
static int count = 0;
}
count milik class.
A
└── count
Bukan:
object B1 → count
object B2 → count
Karena interface terutama menentukan contract instance, static member tidak menjadi kewajiban implementasi object.
7. Kenapa interface tidak bisa diakses menggunakan super?
Misalnya inheritance:
class A {
void test() {
print("A");
}
}
class B extends A {
@override
void test() {
print("B");
super.test();
}
}
Output:
B
A
super.test() berarti:
Jalankan implementasi
test()dari superclass A.
Tetapi dengan implements
class A {
void test() {
print("A");
}
}
class B implements A {
@override
void test() {
print("B");
super.test(); // ❌
}
}
Ini tidak bisa.
Kenapa?
Karena A bukan superclass B.
A hanya menjadi interface.
B tidak mendapatkan implementasi test() dari A.
Jadi:
extends
B ───────→ A
superclass
punya implementasi
implements
B ───────→ A
contract/interface
tidak mewarisi implementasi
Oleh karena itu:
super.test()
tidak memiliki implementasi superclass A yang bisa dipanggil.
8. Bagaimana dengan getter dan setter pada interface?
Getter dan setter juga termasuk instance members.
Misalnya:
class A {
int _x = 10;
int get x => _x;
set x(int value) {
_x = value;
}
}
Jika:
class B implements A {
}
maka B harus menyediakan:
int get x
dan:
set x(int value)
Misalnya:
class B implements A {
int _data = 20;
@override
int get x => _data;
@override
set x(int value) {
_data = value;
}
}
Hal yang perlu diingat
Interface bukan hanya berisi method.
Interface bisa memiliki contract untuk:
- field/property
- getter
- setter
- method
Sedangkan static tidak menjadi kewajiban instance implementation.
9. Bagaimana kalau class implements interface sekaligus extends class?
Ini kasus yang sangat menarik.
Misalnya:
class A {
void test() {
print("A");
}
}
class B {
void test() {
print("B");
}
}
class C extends B implements A {
}
Pertanyaannya:
Apakah C harus menulis ulang
test()?
Tidak harus, selama method warisan dari B sudah memenuhi contract A.
Kenapa?
C mendapatkan:
dari B:
test()
dan interface A meminta:
harus punya:
test()
Method test() yang diwarisi dari B sudah memenuhi kebutuhan tersebut.
Jadi:
C obj = C();
obj.test();
akan menghasilkan:
B
Tetapi C bisa override lagi
class C extends B implements A {
@override
void test() {
print("C");
}
}
Sekarang:
C().test();
menghasilkan:
C
Jadi urutannya secara sederhana:
A → interface contract
B → menyediakan implementasi
C
├── extends B
└── implements A
B.test() sudah memenuhi contract A
Kalau C ingin perilaku sendiri, C bisa override.
10. Bagaimana implements bekerja bersama mixin?
Misalnya:
class A {
void test() {
print("A");
}
}
mixin B {
void test() {
print("B");
}
}
Kemudian:
class C with B implements A {
}
Di sini:
implements A
meminta C mempunyai test().
Sementara:
with B
memasukkan implementasi test() dari mixin B ke C.
Jadi C sudah mendapatkan:
test()
dari mixin B.
Maka C tidak harus menulis ulang test().
C().test();
Output:
B
Kalau C override?
class C with B implements A {
@override
void test() {
print("C");
}
}
Sekarang:
C.test()
yang digunakan.
Ini konsep yang sangat berguna
Kita bisa membayangkan:
Interface
↓
"Harus punya method"
Mixin
↓
"Ini implementasinya"
Class
↓
"Bisa menggunakan implementasi tersebut"
11. Apa yang terjadi jika abstract class mengimplementasikan interface?
Misalnya:
class A {
void test() {
print("A");
}
}
Kemudian:
class B implements A {
}
Ini error karena B concrete tetapi belum memenuhi contract A.
Namun:
abstract class B implements A {
}
boleh.
Kenapa?
Karena abstract class boleh memiliki method yang belum memiliki implementasi konkret.
Kemudian subclass concrete yang mewarisi B harus menyelesaikannya.
Contoh:
abstract class B implements A {
}
class C extends B {
@override
void test() {
print("C");
}
}
Sekarang semuanya valid.
Polanya
A
↓
interface
B
↓
abstract class
↓
boleh belum menyelesaikan contract
C
↓
concrete class
↓
harus menyelesaikan contract
12. Apakah constant dari interface harus di-override?
Nah, bagian transcript ini perlu dikoreksi.
Transcript membedakan:
static const
dan:
final
lalu mengatakan final harus di-override sedangkan const tidak.
Untuk Dart modern, cara berpikir yang lebih aman adalah:
Yang menentukan apakah sebuah member menjadi bagian dari interface adalah apakah member tersebut merupakan instance member atau static member, bukan sekadar apakah dia
finalatauconst.
Contoh:
class A {
static const int x = 10;
}
Ini static, jadi bukan kewajiban instance implementation.
Sedangkan:
class A {
final int x = 10;
}
x adalah instance member, sehingga class yang mengimplementasikan A perlu memenuhi contract tersebut.
Misalnya:
class B implements A {
@override
final int x = 20;
}
Jadi jangan menghafal:
const → tidak override
final → harus override
Itu terlalu menyederhanakan.
Yang lebih penting:
static member
↓
bukan contract instance
instance member
↓
harus dipenuhi oleh concrete implementer
13. Bisakah interface extend class?
Bisa.
Karena di Dart, setiap class juga dapat digunakan sebagai interface.
Contoh:
class A {
void test() {
print("A");
}
}
class B extends A {
void demo() {
print("B");
}
}
Kemudian:
class C implements B {
}
Apa yang sekarang diwajibkan C?
B memiliki:
demo()
dan mewarisi:
test()
dari A.
Karena C menggunakan B sebagai interface, contract yang terlihat melalui B mencakup member instance yang relevan.
Maka C perlu menyediakan:
class C implements B {
@override
void test() {
print("C test");
}
@override
void demo() {
print("C demo");
}
}
Visualisasinya
A
│
│ extends
↓
B
│
│ implements
↓
C
B adalah subclass A.
Tetapi ketika C menulis:
implements B
B diperlakukan sebagai interface/contract.
14. Bisakah interface mengimplementasikan interface lain?
Ya.
Misalnya:
class A {
void test() {}
}
class B implements A {
void demo() {}
}
Kemudian:
class C implements B {
}
Karena B sendiri mengimplementasikan A, maka contract B juga mencakup kebutuhan dari A.
Jadi C perlu memenuhi contract yang diwariskan/dibawa oleh B.
Misalnya:
class C implements B {
@override
void test() {
print("Test C");
}
@override
void demo() {
print("Demo C");
}
}
Visual:
A
│
│ implements
↓
B
│
│ implements
↓
C
15. Concrete class atau abstract class — mana yang bisa menjadi interface?
Keduanya bisa.
Contoh concrete class:
class A {
void test() {}
}
Bisa:
class B implements A {
@override
void test() {}
}
Abstract class juga:
abstract class A {
void test();
}
Bisa:
class B implements A {
@override
void test() {
print("B");
}
}
Jadi:
Concrete class
↓
bisa menjadi interface
Abstract class
↓
bisa menjadi interface
Sekarang kita satukan semuanya
Ini bagian paling penting untuk dipahami.
Ada tiga konsep yang sering tertukar:
extends
class B extends A
Artinya:
B adalah turunan A dan mewarisi implementasi/state yang diwariskan.
A
↓
B
Bisa menggunakan:
super.method();
Constructor superclass juga berperan dalam initialization.
implements
class B implements A
Artinya:
B mengikuti contract A.
B tidak mewarisi implementasi instance dari A.
B harus menyediakan implementation untuk contract instance yang dibutuhkan.
Tidak bisa:
super.method();
untuk mengakses implementasi A.
Constructor A juga tidak otomatis dipanggil.
with
class C with MixinA
Artinya:
C menggunakan implementasi yang diberikan oleh mixin.
Mixin sangat berguna untuk berbagi implementasi tanpa harus menjadikan class tersebut sebagai superclass.
Perbandingan besar
| Konsep | extends | implements | with |
|---|---|---|---|
| Tujuan utama | inheritance | contract/interface | reuse implementation |
| Bisa lebih dari satu? | ❌ satu superclass langsung | ✅ bisa banyak | ✅ bisa beberapa mixin |
| Mendapat implementasi superclass? | ✅ | ❌ | ✅ dari mixin |
super.method() ke sumber tersebut? | ✅ untuk superclass | ❌ interface | konsep super bisa digunakan dalam mixin chain |
| Constructor superclass otomatis terlibat? | ✅ | ❌ | bukan inheritance constructor |
| Harus implement semua contract instance? | Tidak selalu | ✅ untuk concrete class | Tidak semata-mata karena with |
Hubungan dengan polymorphism
Interface menjadi sangat berguna ketika kita ingin membuat program yang fleksibel.
Misalnya:
abstract class Payment {
void pay();
}
Kemudian:
class CreditCardPayment implements Payment {
@override
void pay() {
print("Bayar dengan kartu");
}
}
class BankPayment implements Payment {
@override
void pay() {
print("Bayar dengan bank");
}
}
Kita bisa membuat:
void processPayment(Payment payment) {
payment.pay();
}
Kemudian:
processPayment(CreditCardPayment());
processPayment(BankPayment());
Output:
Bayar dengan kartu
Bayar dengan bank
Perhatikan:
void processPayment(Payment payment)
tidak peduli object konkretnya apa.
Yang penting:
“Object tersebut memenuhi contract
Payment.”
Ini adalah salah satu alasan interface sangat penting dalam OOP.
Contoh yang sangat relevan dengan Flutter
Bayangkan kita mempunyai berbagai sumber data:
abstract class UserRepository {
Future<String> getUser();
}
Kemudian:
class ApiUserRepository implements UserRepository {
@override
Future<String> getUser() async {
return "User dari API";
}
}
Dan:
class LocalUserRepository implements UserRepository {
@override
Future<String> getUser() async {
return "User dari database lokal";
}
}
Kemudian bagian aplikasi cukup bergantung pada:
UserRepository
bukan secara langsung:
ApiUserRepository
Ini sangat berguna untuk:
- testing
- dependency injection
- mengganti API
- mengganti database
- clean architecture
- repository pattern
- memisahkan UI dengan data source
Misalnya saat testing kita bisa membuat:
class FakeUserRepository implements UserRepository {
@override
Future<String> getUser() async {
return "Fake User";
}
}
Tanpa mengubah kode yang menggunakan UserRepository.
Satu hal penting: “interface” di Dart modern
Transcript video ini menggunakan istilah:
“Setiap class otomatis menjadi implicit interface.”
Ini masih berguna untuk memahami konsep, tetapi Dart modern memiliki modifier class khusus seperti interface, base, final, dan sealed yang mengatur bagaimana class boleh digunakan lintas library.
Jadi jangan menganggap Dart modern sama persis dengan Dart versi lama yang digunakan dalam video.
Untuk level pembelajaran sekarang, pegang dulu inti berikut:
class A {
void test() {}
}
Class A dapat digunakan sebagai contract:
class B implements A {
@override
void test() {}
}
Sedangkan:
class B extends A
berarti inheritance.
🔥 Cara paling gampang mengingat 15 pertanyaan tadi
Kalau kamu lupa semuanya, ingat 6 aturan utama ini:
1. extends = mewarisi
class B extends A
B mendapatkan implementasi yang diwariskan dari A.
2. implements = memenuhi contract
class B implements A
B tidak mengambil implementasi A, tetapi harus memenuhi contract instance A.
3. Bisa banyak interface
class C implements A, B, D
Satu class bisa memenuhi banyak contract.
4. Tidak ada constructor inheritance melalui implements
class B implements A
Constructor A tidak otomatis dijalankan.
5. super untuk superclass, bukan interface
class B extends A {
void test() {
super.test();
}
}
Bisa.
Tetapi:
class B implements A {
void test() {
super.test(); // ❌
}
}
Tidak bisa.
6. Abstract class boleh menjadi “belum selesai”
abstract class B implements A {}
Boleh belum memenuhi semua contract karena B sendiri abstract.
Tetapi concrete subclass akhirnya harus menyelesaikannya.
🧠 Mental model terakhir
Bayangkan ada sertifikat/kontrak.
INTERFACE
│
"Kamu wajib punya
method tertentu"
│
┌─────────┴─────────┐
↓ ↓
Class A Class B
implements implements
↓ ↓
implementasi implementasi
versi A versi B
Interface tidak berkata:
“Gunakan kode saya.”
Interface berkata:
“Penuhi aturan saya.”
Sedangkan inheritance:
Superclass A
│
│ extends
↓
Subclass B
│
└── mewarisi implementasi A
Jadi kalimat paling penting dari seluruh materi ini adalah:
extendsfokus pada inheritance/implementasi yang diwariskan, sedangkanimplementsfokus pada contract yang harus dipenuhi.
Dan inilah alasan kenapa interface sangat powerful dalam Flutter: kita bisa membuat kode yang bergantung pada contract, bukan bergantung langsung pada implementasi tertentu.