Masukkan Password
42 Sections for initialization in constructor | Dart Tutorials #41
Transcript ini membahas field initializer, initializer list, constructor body, dan hubungannya dengan redirecting serta factory constructor.
Bagian ini memang agak membingungkan karena beberapa istilahnya mirip. Bahkan ada beberapa bagian transcript yang secara teknis perlu diluruskan. Saya akan mulai dari konsep paling dasar, lalu masuk ke aturan-aturannya satu per satu.
1. Gambaran besar: constructor Dart punya beberapa bagian
Secara sederhana, sebuah constructor bisa memiliki beberapa bagian seperti:
class Point {
Point(this.x, this.y) : ... {
...
}
}
Bagian-bagian yang perlu kamu kenali adalah:
Point
↓
nama constructor
(this.x, this.y)
↓
parameter list / initializing formal
: ...
↓
initializer list atau redirecting constructor
{ ... }
↓
constructor body
Tetapi tidak semua bagian bisa digunakan bersamaan.
Misalnya:
Point(...) : this(...);
adalah redirecting constructor, sehingga tidak boleh memiliki body.
Atau:
Point(...) : x = 10;
menggunakan initializer list.
2. Tiga cara utama menginisialisasi field
Ini inti dari video.
Ada tiga cara yang dibahas:
Cara 1 — Field initializer / initializing formal
class Point {
int x;
int y;
Point(this.x, this.y);
}
Cara 2 — Initializer list
class Point {
int x;
int y;
Point(int a, int b)
: x = a,
y = b;
}
Cara 3 — Constructor body
class Point {
int x;
int y;
Point(int a, int b) {
x = a;
y = b;
}
}
Tetapi ada perbedaan penting antara ketiganya, terutama ketika menggunakan non-nullable field dan final.
3. Pertama: apa itu field?
Misalnya:
class Point {
int x;
int y;
}
x dan y disebut field atau instance variable.
Setiap object Point memiliki field tersebut.
Misalnya:
final p1 = Point();
final p2 = Point();
Secara konsep:
p1
↓
Point object
x = ?
y = ?
p2
↓
Point object
x = ?
y = ?
Field harus mendapatkan nilai sesuai aturan null safety Dart.
4. Cara pertama: field initializer / initializing formal
Perhatikan:
class Point {
int x;
int y;
Point(this.x, this.y);
}
Bagian:
this.x
this.y
dalam parameter constructor disebut initializing formal parameters.
Transcript menyebutnya field initializer dan juga mengatakan ini adalah “synthetic sugar”.
Untuk pemahaman praktis, anggap saja:
this.xpada parameter constructor adalah cara singkat untuk menerima parameter lalu langsung mengisinya ke field.
Contoh:
Point(this.x, this.y);
kurang lebih setara dengan:
Point(int x, int y) {
this.x = x;
this.y = y;
}
Namun secara teknis mekanisme initialization-nya bukan sekadar assignment biasa di body. Itu penting karena berkaitan dengan final dan non-nullable field.
5. Contoh sederhana
class Point {
int x;
int y;
Point(this.x, this.y);
}
Kemudian:
final point = Point(10, 20);
Maka:
point
↓
x = 10
y = 20
10 masuk ke:
this.x
dan 20 masuk ke:
this.y
6. Jangan tertukar this.x dengan x
Ini sangat penting.
Dalam:
Point(this.x, this.y);
this.x adalah field milik object.
Tetapi dalam:
Point(int x, int y)
x dan y adalah parameter lokal.
Misalnya:
Point(int x, int y) {
this.x = x;
this.y = y;
}
Ada dua x:
this.x
↑
field milik object
x
↑
parameter
7. Cara kedua: initializer list
Sekarang kita punya:
class Point {
int x;
int y;
Point(int a, int b)
: x = a,
y = b;
}
Bagian:
: x = a,
y = b
disebut initializer list.
Perhatikan posisi ::
Point(int a, int b)
↑
parameter
:
↑
initializer list
x = a,
y = b
8. Cara kerja initializer list
Ketika:
Point(10, 20);
dipanggil:
a = 10
b = 20
initializer list:
x = a → x = 10
y = b → y = 20
constructor body → kalau ada
Jadi initializer list digunakan untuk menginisialisasi field sebelum constructor body dijalankan.
9. Initializer list bisa digunakan untuk melakukan perhitungan
Misalnya:
class Rectangle {
final int width;
final int height;
final int area;
Rectangle(this.width, this.height)
: area = width * height;
}
Ketika:
final rectangle = Rectangle(10, 5);
hasilnya:
width = 10
height = 5
area = 50
Ini salah satu kegunaan penting initializer list.
10. Cara ketiga: constructor body
Cara ketiga adalah melakukan initialization di dalam {}.
Contoh:
class Point {
int? x;
int? y;
Point(int a, int b) {
x = a;
y = b;
}
}
Di sini:
Point(int a, int b) {
x = a;
y = b;
}
a dan b adalah parameter.
Kemudian di dalam body:
x = a;
y = b;
field diisi.
11. Kenapa constructor body punya masalah dengan non-nullable field?
Sekarang kita ubah:
int? x;
int? y;
menjadi:
int x;
int y;
Kemudian:
class Point {
int x;
int y;
Point(int a, int b) {
x = a;
y = b;
}
}
Dart akan menolak kode tersebut.
Kenapa?
Karena x dan y adalah non-nullable instance field dan harus sudah terinisialisasi pada saat object selesai menjalani fase construction sebelum constructor body dapat dianggap aman.
Untuk field non-nullable, Dart tidak membolehkan object berada dalam keadaan di mana field tersebut belum mendapatkan nilai yang valid selama initialization biasa.
12. Gambaran sederhananya
Bayangkan object dibuat:
Point object dibuat
↓
field x dibuat
field y dibuat
↓
initializer / initialization
↓
constructor body
Kalau:
int x;
int y;
Dart ingin memastikan initialization-nya dilakukan pada tahap yang benar.
Jadi:
Point(int a, int b) {
x = a;
y = b;
}
tidak cukup untuk memenuhi aturan initialization non-nullable field.
13. Solusi pertama: initializing formal
Gunakan:
class Point {
int x;
int y;
Point(this.x, this.y);
}
Sekarang valid.
Karena x dan y langsung diinisialisasi melalui constructor parameter.
14. Solusi kedua: initializer list
Bisa juga:
class Point {
int x;
int y;
Point(int a, int b)
: x = a,
y = b;
}
Ini juga valid.
Karena x dan y diinisialisasi melalui initializer list.
15. Bagaimana kalau menggunakan final?
Misalnya:
class Point {
final int x;
final int y;
Point(this.x, this.y);
}
Ini valid.
Bahkan ini merupakan pola yang sangat umum.
Karena:
final int x;
harus diberi nilai sekali dan tidak boleh diubah lagi.
Constructor adalah tempat yang cocok untuk memberikan nilai tersebut.
16. final tidak boleh diisi di constructor body
Misalnya:
class Point {
final int x;
final int y;
Point(int a, int b) {
x = a; // ❌
y = b; // ❌
}
}
Ini tidak boleh.
Karena final instance field harus diinisialisasi melalui:
- deklarasi field,
- initializing formal,
- initializer list,
- atau mekanisme initialization lain yang sesuai.
Contoh yang benar:
class Point {
final int x;
final int y;
Point(this.x, this.y);
}
atau:
class Point {
final int x;
final int y;
Point(int a, int b)
: x = a,
y = b;
}
17. Lalu apa gunanya late?
Nah, ini yang dibahas transcript.
Misalnya:
class Point {
late int x;
late int y;
Point(int a, int b) {
x = a;
y = b;
}
}
Ini bisa.
Kenapa?
Karena late memberi tahu Dart:
“Field ini memang non-nullable, tetapi saya berjanji akan mengisinya nanti sebelum digunakan.”
Jadi:
late int x;
berbeda dengan:
int x;
18. late bukan berarti nullable
Ini penting.
late int x;
bukan berarti:
x boleh null
Tetapi:
x tidak boleh null ketika digunakan,
namun initialization-nya ditunda.
Kalau kamu melakukan:
class Point {
late int x;
}
void main() {
final p = Point();
print(p.x); // ❌ runtime error
}
akan terjadi error karena x belum diinisialisasi.
19. Bandingkan nullable dan late
Nullable
int? x;
Artinya:
x boleh memiliki nilai null
Late non-nullable
late int x;
Artinya:
x tidak boleh null,
tetapi initialization ditunda
Non-nullable biasa
int x;
Artinya:
x harus diinisialisasi pada tahap construction yang sesuai
20. Timeline constructor
Ini bagian yang sangat penting untuk memahami semuanya.
Secara sederhana, bayangkan urutannya seperti ini:
Object mulai dibuat
↓
Field initialization
↓
Initializer list
↓
Constructor body
↓
Object siap digunakan
Karena itu initializer formal dan initializer list cocok untuk field yang harus sudah memiliki nilai sejak proses construction.
21. Tapi ada satu koreksi terhadap transcript
Transcript mengatakan kira-kira:
“Field initializer dan initializer list execute sebelum body.”
Ini sebagai gambaran pemula boleh digunakan, tetapi jangan membayangkan object secara harfiah “sudah selesai dibuat lalu initializer dijalankan”.
Lebih tepat:
Dart memiliki fase initialization sebelum constructor body dijalankan, dan field/initializer list menjadi bagian dari proses initialization tersebut.
Ini penting terutama ketika membahas inheritance, super, final, dan late.
22. Apakah field initializer dan initializer list bisa digunakan bersamaan?
Dalam konteks yang dimaksud transcript, tidak bisa menggunakan initializing formal this.x untuk field yang sama sekaligus menginisialisasi field itu lagi melalui initializer list.
Misalnya:
class Point {
int x;
int y;
Point(this.x, this.y)
: x = 10,
y = 20; // ❌
}
Ini bermasalah karena x dan y sudah diinisialisasi melalui:
this.x
this.y
kemudian dicoba diinisialisasi lagi:
x = 10
y = 20
Jadi jangan melakukan dua initialization terhadap field yang sama.
23. Pilih salah satu
Pilihan A — initializing formal
class Point {
int x;
int y;
Point(this.x, this.y);
}
Pilihan B — initializer list
class Point {
int x;
int y;
Point(int a, int b)
: x = a,
y = b;
}
Keduanya bisa digunakan untuk tujuan initialization.
24. Tetapi parameter biasa tetap bisa dipakai dengan initializer list
Ini valid:
class Point {
int x;
int y;
Point(int a, int b)
: x = a,
y = b;
}
Perhatikan:
int a
int b
hanya parameter biasa.
Kemudian:
x = a
y = b
adalah initializer list.
Jadi:
a, b
↓
parameter
x = a
y = b
↓
initializer list
25. Initializer list juga bisa digunakan tanpa parameter
Misalnya:
class Point {
int x;
int y;
Point()
: x = 10,
y = 20;
}
Ketika:
Point();
hasilnya:
x = 10
y = 20
26. Kalau constructor hanya melakukan initialization, body tidak wajib
Misalnya:
class Point {
final int x;
final int y;
Point(this.x, this.y);
}
Tidak ada:
{
...
}
Kenapa?
Karena constructor hanya bertugas menginisialisasi field.
Jadi cukup:
Point(this.x, this.y);
Perhatikan tanda:
;
di akhir.
27. Kalau ada pekerjaan tambahan, gunakan body
Misalnya:
class Point {
final int x;
final int y;
Point(this.x, this.y) {
print("Point dibuat");
}
}
Di sini initialization dilakukan oleh:
this.x
this.y
Kemudian ada pekerjaan tambahan:
print("Point dibuat");
Maka body digunakan.
28. Contoh kombinasi yang valid
Kamu bisa memiliki initializing formal + initializer list + body.
Misalnya:
class Rectangle {
final int width;
final int height;
final int area;
Rectangle(this.width, this.height)
: area = width * height {
print("Rectangle dibuat");
}
}
Perhatikan:
Rectangle(this.width, this.height)
↓
initializing formal
: area = width * height
↓
initializer list
{
print(...)
}
↓
body
Ini valid.
Jadi bukan berarti ketiga teknik initialization tidak boleh muncul dalam satu constructor.
Yang tidak boleh adalah menginisialisasi field yang sama dua kali, dan ada aturan khusus untuk redirecting constructor.
29. Redirecting constructor berbeda lagi
Misalnya:
class Point {
final int x;
final int y;
Point(this.x, this.y);
Point.origin() : this(0, 0);
}
Point.origin() adalah redirecting constructor.
Dia berkata:
Point.origin()
↓
this(0, 0)
↓
Point(this.x, this.y)
30. Redirecting constructor tidak boleh punya initializer list sendiri
Misalnya:
class Point {
final int x;
final int y;
Point(this.x, this.y);
Point.origin()
: this(0, 0),
x = 10; // ❌
}
Tidak boleh.
Karena this(0, 0) sendiri sudah merupakan redirect.
Jadi redirecting constructor tidak bisa sekaligus mengatakan:
redirect ke constructor A
dan
saya juga mau melakukan initialization sendiri
31. Redirecting constructor juga tidak boleh punya body
Ini juga:
class Point {
Point(this.x, this.y);
Point.origin() : this(0, 0) {
print("Hello"); // ❌
}
}
Tidak boleh.
Bentuknya harus selesai pada:
Point.origin() : this(0, 0);
32. Kenapa?
Karena redirecting constructor menyerahkan proses construction kepada constructor tujuan.
Bayangkan:
Point.origin()
↓
"Silakan constructor Point(...) yang mengurus semuanya."
Jadi Point.origin() tidak boleh ikut melakukan initialization/body sendiri.
33. Bandingkan tiga constructor ini
Ini sangat bagus untuk mengikat semua konsep.
A. Generative + initializing formal
Point(this.x, this.y);
B. Generative + initializer list
Point(int x, int y)
: this.x = x,
this.y = y;
C. Redirecting
Point.origin() : this(0, 0);
Perbedaannya:
A
→ saya sendiri menginisialisasi field
B
→ saya sendiri menginisialisasi field melalui initializer list
C
→ saya menyerahkan construction ke constructor lain
34. Factory tidak boleh menggunakan initializing formal
Ini juga dibahas dalam transcript.
Misalnya:
class Point {
int x;
int y;
factory Point(this.x, this.y) { // ❌
return Point(...);
}
}
Tidak boleh.
Karena factory tidak melakukan generative initialization terhadap instance this.
Factory tidak punya this instance.
35. Factory juga tidak boleh memiliki initializer list
Misalnya:
class Point {
final int x;
final int y;
factory Point.create()
: x = 10,
y = 20 { // ❌
return Point(...);
}
}
Tidak boleh.
Factory constructor tidak menggunakan initializer list.
36. Factory harus menggunakan body
Factory biasanya seperti:
class Point {
final int x;
final int y;
Point(this.x, this.y);
factory Point.create() {
return Point(10, 20);
}
}
Perhatikan:
factory
↓
body
↓
return instance
Ini berbeda dari redirecting constructor.
37. Bandingkan factory dan redirecting
Ini wajib kamu kuasai.
Redirecting
Point.origin() : this(0, 0);
Strukturnya:
constructor
↓
:
↓
this(...)
Tidak ada body.
Factory
factory Point.origin() {
return Point(0, 0);
}
Strukturnya:
factory
↓
body
↓
return
38. Keduanya terlihat mirip, tapi berbeda
Perhatikan:
Point.origin() : this(0, 0);
vs:
factory Point.origin() {
return Point(0, 0);
}
Keduanya mungkin menghasilkan object yang sama.
Tetapi:
Yang pertama
redirecting generative constructor
Yang kedua
factory constructor
39. Kenapa redirecting constructor lebih sederhana?
Karena dia hanya berkata:
“Gunakan constructor ini.”
Contoh:
Point.origin() : this(0, 0);
Tidak bisa:
if
else
print
logic
cache
return object lain
Sedangkan factory:
factory Point.create(int value) {
if (value > 10) {
return Point(10, 10);
}
return Point(0, 0);
}
Factory jauh lebih fleksibel.
40. Hubungan dengan final
Ini bagian yang sangat penting untuk Flutter/Dart modern.
Misalnya model:
class User {
final String name;
final int age;
User(this.name, this.age);
}
Ini sangat bagus.
Kenapa?
Karena setelah object dibuat:
final user = User("Budi", 20);
nilai:
name = Budi
age = 20
tidak bisa diubah.
Misalnya:
user.name = "Andi"; // ❌
Tidak boleh karena name adalah final.
41. Kenapa pola ini sering digunakan?
Karena object menjadi lebih aman dan mudah diprediksi.
Misalnya:
class User {
final String id;
final String name;
User(this.id, this.name);
}
Begitu object dibuat:
final user = User("001", "Budi");
kita tahu:
id → selalu "001"
name → selalu "Budi"
Selama tidak ada mekanisme lain seperti getter dari sumber mutable, field tersebut tidak berubah.
42. Initializer list sangat berguna untuk final
Contoh:
class Circle {
final double radius;
final double area;
Circle(this.radius)
: area = 3.14 * radius * radius;
}
Kita tidak bisa melakukan:
Circle(this.radius) {
area = 3.14 * radius * radius; // ❌
}
karena area adalah final.
Tetapi initializer list:
: area = ...
memungkinkan initialization dilakukan pada tahap yang tepat.
43. Bahkan bisa menggunakan logic/function
Misalnya:
int calculateArea(int radius) {
return 3 * radius * radius;
}
class Circle {
final int radius;
final int area;
Circle(this.radius)
: area = calculateArea(radius);
}
Jadi initializer list bukan hanya:
x = 10
Bisa juga:
x = someFunction(...)
atau perhitungan:
x = a + b
44. Urutan sederhananya
Bayangkan constructor:
class Example {
final int x;
final int y;
Example(int a, int b)
: x = a,
y = b {
print("Body");
}
}
Ketika:
Example(10, 20);
secara konseptual:
1. Parameter diterima
a = 10
b = 20
2. Field initialization
x = 10
y = 20
3. Constructor body
print("Body")
4. Construction selesai
Output:
Body
45. Contoh membandingkan semua metode
A. Initializing formal
class Point {
int x;
int y;
Point(this.x, this.y);
}
B. Initializer list
class Point {
int x;
int y;
Point(int a, int b)
: x = a,
y = b;
}
C. Body
Bisa untuk field nullable atau late:
class Point {
late int x;
late int y;
Point(int a, int b) {
x = a;
y = b;
}
}
46. Jangan menyamakan late dengan solusi utama
Walaupun ini bisa:
late int x;
jangan berpikir:
“Kalau ada non-nullable field, tinggal kasih
late.”
Tidak selalu.
Lebih baik:
class Point {
final int x;
final int y;
Point(this.x, this.y);
}
daripada:
class Point {
late int x;
late int y;
Point(int a, int b) {
x = a;
y = b;
}
}
Yang pertama lebih aman karena Dart dapat memastikan initialization terjadi dalam proses construction.
47. Kesalahan umum pemula
Kesalahan 1
class User {
final String name;
User(String name) {
this.name = name; // ❌
}
}
Gunakan:
User(this.name);
atau:
User(String name) : this.name = name;
Kesalahan 2
Menganggap:
int? x;
dan:
late int x;
sama.
Tidak sama.
int? x
→ boleh null
late int x
→ tidak boleh null ketika digunakan, tetapi initialization ditunda
Kesalahan 3
Menganggap factory bisa:
factory User(this.name); // ❌
Tidak bisa.
Kesalahan 4
Mencampurkan redirecting dan initializer list:
User.guest() : this(), name = "Guest"; // ❌
Redirecting constructor tidak boleh memiliki initialization sendiri.
48. Cheat sheet
| Bentuk | Nama | Kegunaan |
|---|---|---|
Person(this.name) | Initializing formal | Langsung isi field dari parameter |
Person(String name) : this.name = name | Initializer list | Inisialisasi field sebelum body |
Person(String name) { ... } | Constructor body | Logic tambahan / assignment untuk field yang memang boleh diinisialisasi di body |
Person.guest() : this(...) | Redirecting constructor | Mengalihkan ke constructor lain |
factory Person(...) { return ...; } | Factory constructor | Memilih/menghasilkan instance |
49. Hubungkan dengan materi sebelumnya
Sekarang kalau digabung dengan materi constructor yang sudah kita bahas, gambarnya menjadi seperti ini:
CONSTRUCTOR
│
┌──────────────┴──────────────┐
│ │
Generative Factory
│ │
│ └── tidak punya this
│ tidak punya initializer list
│ harus return instance
│
┌─────┴─────────┐
│ │
│ │
Initialization Redirecting
│ │
│ └── : this(...)
│
┌──┴──────────────┐
│ │
Initializing Initializer
formal list
│ │
│ │
this.x x = ...
this.y y = ...
50. Contoh yang sangat bagus untuk Flutter
Misalnya kamu membuat model:
class Product {
final String name;
final double price;
final double discountPrice;
Product(
this.name,
this.price,
this.discountPrice,
);
Product.discounted(
String name,
double price,
) : this(
name,
price,
price * 0.9,
);
}
Sekarang:
final product = Product.discounted("Laptop", 10_000_000);
Redirecting constructor:
Product.discounted(...)
↓
this(...)
↓
Product(
"Laptop",
10000000,
9000000
)
Hasil:
name = Laptop
price = 10.000.000
discountPrice = 9.000.000
Tidak perlu menulis logic initialization berkali-kali.
51. Kesimpulan paling penting
Kalau materi ini terasa banyak, cukup kuasai urutan berikut.
① Initializing formal
User(this.name, this.age);
Artinya:
Parameter langsung digunakan untuk menginisialisasi field.
② Initializer list
User(String name, int age)
: this.name = name,
this.age = age;
Artinya:
Field diinisialisasi sebelum constructor body.
Ini sangat berguna untuk final, non-nullable field, dan perhitungan initialization.
③ Constructor body
User(String name) {
print(name);
}
Artinya:
Jalankan logic setelah proses initialization.
Untuk non-nullable/final field, jangan mengandalkan assignment biasa di body; gunakan initialization yang sesuai.
④ Redirecting constructor
User.guest() : this("Guest", 0);
Artinya:
Constructor ini menyerahkan construction kepada constructor lain di class yang sama.
Tidak boleh ada body atau initializer list sendiri.
⑤ Factory constructor
factory User.guest() {
return User("Guest", 0);
}
Artinya:
Factory menjalankan logic dan mengembalikan instance.
Factory tidak memiliki this dan tidak menggunakan initializer list.
🧠 Kalimat hafalan
this.xdi parameter → langsung mengisi field.: x = ...→ initializer list.{ ... }→ constructor body.: this(...)→ redirect ke constructor lain.factory ... { return ... }→ factory yang memilih/mengembalikan instance.
Dan satu koreksi paling penting dari transcript: initializer list bukan satu-satunya cara untuk menghitung nilai final. Kamu bisa melakukan perhitungan di initializer list, tetapi generative constructor juga punya initializing formal dan bentuk initialization lain yang sah. Sedangkan factory memberikan fleksibilitas berbeda karena dia bisa menghitung sesuatu di body lalu return instance yang dipilih.