0% menganggap dokumen ini bermanfaat (0 suara)
45 tayangan

Paradigma Object Oriented Programming

1. Dokumen tersebut membahas tentang paradigma pemrograman berorientasi objek (OOP) yang mencakup 4 pilar utamanya yaitu enkapsulasi, abstraksi, pewarisan, dan polimorfisme. 2. OOP berdasarkan konsep objek yang memiliki atribut dan perilaku, dan Dart mendukung pemrograman berorientasi objek dengan menggunakan class. 3. Class digunakan untuk mendefinisikan atribut dan perilaku objek, sedangkan objek dibuat dengan

Diunggah oleh

Susanto Yang
Hak Cipta
© © All Rights Reserved
Format Tersedia
Unduh sebagai DOCX, PDF, TXT atau baca online di Scribd
0% menganggap dokumen ini bermanfaat (0 suara)
45 tayangan

Paradigma Object Oriented Programming

1. Dokumen tersebut membahas tentang paradigma pemrograman berorientasi objek (OOP) yang mencakup 4 pilar utamanya yaitu enkapsulasi, abstraksi, pewarisan, dan polimorfisme. 2. OOP berdasarkan konsep objek yang memiliki atribut dan perilaku, dan Dart mendukung pemrograman berorientasi objek dengan menggunakan class. 3. Class digunakan untuk mendefinisikan atribut dan perilaku objek, sedangkan objek dibuat dengan

Diunggah oleh

Susanto Yang
Hak Cipta
© © All Rights Reserved
Format Tersedia
Unduh sebagai DOCX, PDF, TXT atau baca online di Scribd
Anda di halaman 1/ 42

Paradigma Object Oriented Programming

Pada modul awal kita sudah mengetahui bahwa Dart adalah bahasa yang
mendukung pemrograman berorientasi objek. OOP adalah paradigma
pemrograman yang banyak digunakan saat ini untuk mengembangkan
aplikasi. 

Paradigma OOP berdasarkan pada konsep objek yang memiliki atribut serta
dapat melakukan operasi atau prosedur tertentu. Dengan OOP, kita bisa
dengan mudah memvisualisasikan objek yang ada di dunia nyata ke dalam
program komputer. Sebagai contoh, bayangkan kucing sebagai sebuah
objek. Objek kucing ini memiliki karakteristik seperti warna bulu, usia kucing,
dan berat badan. Ciri-ciri ini disebut dengan attributes atau properties. Selain
itu kucing juga bisa melakukan beberapa hal seperti makan, tidur, dan
bermain. Perilaku pada objek kucing ini adalah sebuah method.

Terdapat 4 (empat) pilar dalam pemrograman berorientasi objek, antara


lain: encapsulation, abstraction, inheritance, dan polymorphism.

Encapsulation

Enkapsulasi adalah kondisi di mana status atau kondisi di dalam class,


dibungkus dan bersifat privat. Artinya objek lain tidak bisa mengakses atau
mengubah nilai dari property secara langsung. Pada contoh kasus kucing kita
tidak bisa langsung mengubah berat badan dari kucing, namun kita bisa
menambahkannya melalui fungsi atau method makan.

Abstraction

Abstraksi bisa dibilang merupakan penerapan alami dari enkapsulasi.


Abstraksi berarti sebuah objek hanya menunjukkan operasinya secara high-
level. Misalnya kita cukup tahu bagaimana seekor kucing makan, namun kita
tidak perlu tahu seperti apa metabolisme biologis dalam tubuh kucing yang
membuat berat badannya bertambah.

Inheritance

Beberapa objek bisa memiliki beberapa karakteristik atau perilaku yang


sama, namun mereka bukanlah objek yang sama. Di sinilah inheritance atau
pewarisan berperan. Kucing memiliki sifat dan perilaku yang umum dengan
hewan lain, seperti memiliki warna, berat, dsb. Maka dari itu kucing sebagai
objek turunan (subclass) mewarisi semua sifat dan perilaku dari objek
induknya (superclass). Begitu juga dengan objek ikan juga mewarisi sifat dan
perilaku yang sama, namun ikan bisa berenang sementara kucing tidak.

Polymorphism

Polymorphism dalam bahasa Yunani berarti “banyak bentuk.” Sederhananya


objek dapat memiliki bentuk atau implementasi yang berbeda-beda pada satu
metode yang sama. Semua hewan bernafas, namun tentu kucing dan ikan
memiliki cara bernafas yang berbeda. Perbedaan bentuk atau cara
pernafasan tersebut merupakan contoh dari polymorphism.

Class
Salah satu fitur utama dari OOP adalah fitur seperti class. Class merupakan
sebuah blueprint untuk membuat objek. Di dalam kelas ini kita mendefinisikan
sifat (attribute) dan perilaku (behaviour) dari objek yang akan dibuat. Sebagai
contoh kelas Animal memiliki atribut berupa nama, berat, dan umur, dll.
Kemudian perilakunya adalah makan, tidur, dsb.

Setiap kelas memiliki attribute dan behaviour. Pada Dart kita


mendefinisikan attribute dengan variabel, sedangkan behaviour sering
direpresentasikan sebagai function.

Animal

+ String name
+ int age
+ double weight

- eat()
- sleep()
- poop()

Untuk mendefinisikan kelas dalam Dart, cukup gunakan keyword class diikuti


dengan nama kelas yang akan dibuat.
1. class Animal {

2. }

Kemudian kita bisa menambahkan variabel dan fungsi pada kelas tersebut.
1. class Animal {

2.   String name;
3.   int age;

4.   double weight;

5.  

6.   Animal(this.name, this.age, this.weight);

7.  

8.   void eat() {

9.     print('$name is eating.');

10.     weight = weight + 0.2;

11.   }

12.   

13.   void sleep() {

14.     print('$name is sleeping.');

15.   }

16.  

17.  

18.   void poop() {

19.     print('$name is pooping.');

20.     weight = weight - 0.1;

21.   }

22. }

Kemudian untuk membuat sebuah objek dari suatu class, gunakan sintaks


berikut:
1. var nameOfObject = NameOfClass(property1, property2);

Sebuah objek sama seperti variabel, artinya kita bisa


menggunakan var untuk menginisialisasikan sebuah objek. Objek yang
disimpan ke dalam variabel ini juga dikenal dengan instance atau instance of a
class. Instance ini menyimpan reference atau alamat memori dari objek. Proses
membuat variabel instance seperti di atas disebut instansiasi (instantiation). 
1. var dicodingCat = Animal('Gray', 2, 4.2);

Kita menggunakan nama kelas serta diikuti dengan tanda kurung. Di dalam
tanda kurung ini kita bisa memasukkan parameter untuk menginisialisasi nilai
di dalam objek. Tanda kurung ini merupakan sebuah constructor yang akan
kita pelajari pada materi selanjutnya.

Setelah objek terbuat kita bisa menjalankan fungsi atau menampilkan nilai
dari property yang ada di dalamnya.
1. void main() {

2.   var dicodingCat = Animal('Gray', 2, 4.2);

3.   dicodingCat.eat();

4.   dicodingCat.poop();

5.   print(dicodingCat.weight);

6. }

Ketika program di atas dijalankan, konsol akan menampilkan hasil sebagai


berikut:
1. Gray is eating.

2. Gray is pooping.

3. 4.300000000000001

Properties & Methods


Kita telah mempelajari variabel untuk menyimpan nilai dan function untuk
menuliskan set instruksi yang bisa digunakan kembali. Di dalam class,
variabel dan fungsi ini dikenal dengan property dan method.
Seperti yang telah disebutkan pada materi Class, property merepresentasikan
atribut pada sebuah objek sementara method menggambarkan perilaku dari
objek.

Sama seperti variabel, kita mendeklarasikan property di dalam kelas dengan


menentukan tipe datanya atau menginisialisasikan nilainya secara eksplisit.
1. class Animal {

2.   String _name = '';

3.   int _age = 0;

4.   double _weight = 0;

5. }

OOP memiliki konsep enkapsulasi di mana kita bisa menyembunyikan


informasi di dalam objek sehingga status atau data di dalam objek tidak bisa
diubah atau bahkan dilihat. Umumnya bahasa pemrograman memiliki visibility
modifier untuk menentukan siapa saja yang bisa
mengakses property atau method. Namun, Dart tidak memiliki keyword visibility
modifier seperti private atau public. Bagaimana cara  mengatasinya?

Jadikanlah class sebagai library lalu panggilah ia dengan keyword import.


Untuk membuat class sebagai library Anda cukup membuat berkas baru,
sehingga Anda akan memiliki 2 buah berkas:

 main.dart
 Animal.dart
1. import 'Animal.dart';

2.  

3. void main() {

4.   var dicodingCat = Animal('Gray', 2, 4.2);

5.  

6.   dicodingCat.eat();

7.   dicodingCat.poop();
8.   print(dicodingCat.weight);

9. }

Property yang private artinya hanya bisa diakses pada berkas atau library yang


sama. Kita akan membutuhkan private property ini di saat kita tidak ingin objek
diubah dari luar. Karena Dart tidak memiliki modifier private, sebagai gantinya
kita perlu menambahkan underscore (_) sebelum nama property.
1. String _name = '';

2. int _age = 0;

3. double _weight = 0;

Setelah menambahkan underscore pada nama variabel, Anda akan


mendapatkan eror di berkas main.dart ketika mengakses property  weight. Apa
pasal? Kini  weight bersifat private dan tidak bisa diakses dari luar berkasnya.
Solusinya, Anda bisa menambahkan setter dan getter untuk mendapatkan nilai
serta mengubahnya dari luar berkas. Jika menggunakan IntelliJ IDEA Anda
bisa menggunakan shortcut Alt + Insert lalu pilih Getter and Setter.
1. // Setter

2. set name(String value) {

3.   _name = value;

4. }

5.  

6. // Getter

7. double get weight => _weight;

Selain dengan setter, Anda juga bisa mengubah nilai dengan property dari


pemanggilan method. Pada contoh kelas hewan tentunya kita tidak bisa
langsung mengubah nilai berat badan, namun kita bisa menambah dan
mengubah nilainya melalui proses makan atau buang air besar (BAB).
1. void eat() {

2.   print('$_name is eating.');

3.   _weight = _weight + 0.2;

4. }

5.  

6. void poop() {

7.   print('$_name is pooping.');

8.   _weight = _weight - 0.1;

9. }

Sehingga, keseluruhan kode pada berkas Animal.dart akan menjadi seperti


berikut.
1. class Animal {

2.   String _name = '';

3.   int _age = 0;

4. double _weight = 0;

5.  

6.   Animal(this._name, this._age, this._weight);

7.  

8.   // Setter

9.   set name(String value) {

10.     _name = value;

11.   }

12.

13.   // Getter

14. double get weight => _weight;


15.  

16.   void eat() {

Constructor
Ketika suatu objek dibuat, semua properti pada kelas tersebut harus memiliki
nilai. Kita dapat langsung menginisialisasi pada properti tertentu atau
menginisialisasinya melalui constructor. Constructor adalah fungsi spesial
dari sebuah kelas yang digunakan untuk membuat objek. 

Sesuai namanya, constructor digunakan untuk mengonstruksi objek baru.

Jadi kenapa constructor disebut sebagai fungsi yang spesial? Apa bedanya


dengan fungsi lain pada class? Beberapa perbedaan antara constructor dan
fungsi biasa adalah:

1. Constructor memiliki nama yang sama dengan nama kelas.


2. Constructor tidak memiliki nilai kembalian (return type).
3. Constructor akan secara otomatis dipanggil ketika sebuah objek dibuat.
4. Jika kita tidak mendefinisikan constructor, default constructor tanpa argumen
akan dibuat.

Pada materi sebelumnya, secara tidak langsung Anda telah belajar


membuat constructor  yang menerima beberapa argumen. Namun, secara
default sebuah constructor  pada kelas tidak menerima argumen apapun.
Sebagai contoh pada kelas Animal akan menjadi seperti berikut:
1. class Animal {

2.   String name = '';

3.   int age = 0;

4.   double weight = 0;

5. }

Untuk membuat objek baru dari kelas tersebut, Anda tidak perlu memberikan
argumen apapun.
1. var dicodingCat = Animal();
Karena kita tidak memasukkan nilai ketika membuat objek, maka
nilai default dari properti atau variabel akan digunakan. Anda perlu berhati-hati
jika tidak memberikan nilai pada properti, karena akan membuat properti
bernilai null sehingga bisa menyebabkan eror.

Untuk memberikan nilai pada properti, silakan akses properti yang ada di
dalam sebuah kelas.
1. var dicodingCat = Animal();

2. dicodingCat.name = 'Gray';

3. dicodingCat.age = 2;

4. dicodingCat.weight = 4.2;

Dengan membuat constructor, kita tidak hanya bisa menginisialisasikan nilai


namun juga menjalankan instruksi tertentu ketika objek dibuat.
1. Animal(String name, int age, double weight) {

2.   this.name = name;

3.   this.age = age;

4.   this.weight = weight;

5.   // other instructions

6. }

Keyword this di atas menunjuk pada objek yang ada di kelas


tersebut. Keyword this ini umumnya digunakan untuk menghindari ambiguitas
antara atribut dari class dan parameter yang memiliki nama yang sama.

Jika constructor hanya digunakan untuk menginisialisasi nilai properti, maka


kode konstruktor dapat diringkas menjadi seperti berikut:
1. Animal(this.name, this.age, this.weight);

Named Constructor
Pada beberapa kasus kita mungkin akan membutuhkan
beberapa constructor untuk skenario yang berbeda-beda. Pada situasi ini kita
bisa memanfaatkan named constructor.

Dengan menggunakan named constructor, kita dapat membuat


beberapa constructor pada kelas yang sama. Setiap constructor akan memiliki
nama yang unik.
1. class_name.constructor_name (arguments){

2.   // Statements

3. }

Contoh pada class Animal adalah seperti berikut:


1. Class Animal {

2.   ...

3.   Animal.Name(this._name);

4.   Animal.Age(this._age);

5.   Animal.Weight(this._weight);

6.   ...

7. }

Cascade Notation
Dart juga dilengkapi dengan cascade notation atau cascade operator.
Operator ini memungkinkan kita untuk melakukan beberapa urutan operasi
pada objek yang sama. Kita bisa mengakses property dari object dan
menjalankan method di dalamnya bersamaan ketika kita menginstansiasi
object. Cascade operator dituliskan dengan dua tanda titik (.. atau ?..).

Perhatikan contoh kode yang menggunakan cascade operator berikut


berikut:
1. void main() {

2.   var dicodingCat = Animal('', 2, 4.2)


3.     ..name = 'Gray'

4.     ..eat();

5. }

Apakah Anda mengerti maksud kode di atas? Kita menginstansiasi object


Animal dengan constructor seperti biasa. Kemudian cascade operator yang
mengikutinya akan melakukan operasi berdasarkan object yang dikembalikan
oleh constructor. Contoh kode tersebut melakukan hal yang sama apabila
kita menuliskan kode seperti ini:
1. var dicodingCat = Animal('', 2, 4.2);

2. dicodingCat.name = 'Gray';

3. dicodingCat.eat();

Cascade operator ini sering kali menghemat langkah dalam membuat


variabel sementara, sehingga kode yang kita tulis menjadi lebih ringkas.
1. Animal('', 2, 4.2)

2.   ..name = 'Gray'

3.   ..eat();

4.  

5.  

6. /* output

7.   Gray is eating.

8.  */

Cascade notation juga akan sering kita temui pada builder pattern seperti ini:
1. final addressBook = (AddressBookBuilder()

2.       ..name = 'jenny'

3.       ..email = '[email protected]'
4.       ..phone = '415-555-0100')

5.     .build();

Inheritance
Beberapa objek bisa memiliki beberapa karakteristik atau perilaku yang
sama, namun sebenarnya mereka bukanlah objek yang sama. Di sini
hadirlah peran inheritance atau pewarisan. Apa definisi
keduanya? Inheritance adalah kemampuan suatu program untuk membuat
kelas baru dari kelas yang ada. Konsep inheritance ini bisa dibayangkan
layaknya seorang anak mewarisi sifat dari orang tuanya. Di dalam OOP kelas
yang menurunkan sifat disebut sebagai kelas induk (parent class/superclass)
sementara kelas yang mewarisi kelas induknya disebut sebagai kelas anak
(child class/subclass).

Yuk kembali lagi pada contoh objek kucing. Selain kucing ada jenis hewan
lain yang bersifat sama. Misalnya ikan dan burung juga memiliki nama, berat,
dan umur. Selain itu mereka juga melakukan aktivitas seperti makan dan
tidur. Yang membedakan objek tersebut adalah cara mereka bernafas dan
bergerak. Untuk lebih memahami, perhatikanlah tabel kelas berikut:

Cat Fish Bird

+ name + name + name


+ weight + weight + weight
+ age + age + age
+ furColor + skinColor + featherColor

- eat() - eat() - eat()


- sleep() - sleep() - sleep()
- poop() - poop() - poop()
- walk() - swim() - fly()

Bisa kita lihat pada tabel di atas bahwa objek Cat, Fish, dan Bird memiliki


beberapa property dan method yang sama seperti name, weight, age, eat(),
dan sleep().

Dibandingkan membuat 3 kelas dan menuliskan ulang properti yang sama,


kita bisa memanfaatkan teknik inheritance dengan mengelompokkan properti
dan fungsi yang sama. Caranya buat sebuah kelas baru yang nantinya akan
diturunkan sifatnya.

Animal
+ name
+ weight
+age

- eat()
- sleep()
- poop()

Cat Fish Bird

+ furColor + skinColor + featherColor

- walk() - swim() - fly()

Setelah membuat kelas Animal, kita dapat membuat kelas lainnya lalu


melakukan extends ke kelas induknya. Untuk
menerapkan inheritance gunakan keyword extends seperti contoh berikut:
1. class ChildClass extends ParentClass {

2.  

3. }

Dengan begitu kita bisa membuat kelas Cat mewarisi kelas Animal.

 Cat.dart
 Animal.dart
1. import 'Animal.dart';

2.  

3. class Cat extends Animal {

4.   late String furColor;

5.  

6.   Cat(String name, int age, double weight, String furColor) :

super(name, age, weight) {

7.     this.furColor = furColor;
8.   }

9.  

10.   void walk() {

11.     print('$name is walking');

12.   }

13.  

14. }

Karena kelas Cat adalah turunan dari kelas Animal, maka kita bisa


mengakses sifat dan perilaku dari Animal melalui kelas Cat.
1. import 'Cat.dart';

2.  

3. void main() {

4.   var dicodingCat = Cat('Grayson', 2, 2.2, 'Gray');

5.   dicodingCat.walk();

6.   dicodingCat.eat();

7.   print(dicodingCat.weight);

8. }

9.  

10. /*

11. Output :

12.   Grayson is walking

13.   Grayson is eating.

14.   2.4000000000000004

15.  */
Inheritance constructor

Karena kelas Animal memiliki constructor untuk menginisialisasi properti di


dalamnya, maka semua kelas turunannya juga perlu
mengimplementasikan constructor tersebut. Oleh sebab itu ketika membuat
kelas Cat tanpa mendefinisikan constructor kita akan mendapatkan eror.
IntelliJ IDEA akan memberikan saran untuk membuat constructor.
1. Cat(String name, int age, double weight) : super(name, age, weight);

Keyword super di atas akan diarahkan ke constructor dari kelas Animal.

Jika ingin menginisialisasikan nilai furColor melalui constructor, maka kita bisa


menambahkan parameter di dalam constructor.
1. Cat(String name, int age, double weight, String furColor) :

super(name, age, weight) {

2.   this.furColor = furColor;

3. }

Atau, kita bisa meringkasnya seperti yang telah kita pelajari pada
materi constructor.
1. Cat(String name, int age, double weight, this.furColor) : super(name,

age, weight);

Abstract Class
Sesuai namanya, abstract merupakan gambaran umum dari sebuah kelas. Ia
tidak dapat direalisasikan dalam sebuah objek. Pada modul sebelumnya kita
sudah mempunyai kelas Animal. Secara harfiah hewan merupakan sebuah
sifat. Kita tidak tahu bagaimana objek hewan tersebut. Kita bisa melihat
bentuk kucing, ikan, dan burung namun tidak untuk hewan. Maka dari itu
konsep abstract class perlu diterapkan agar kelas Animal tidak dapat
direalisasikan dalam bentuk objek namun tetap dapat menurunkan sifatnya
kepada kelas turunannya.
Untuk menjadikan sebuah kelas menjadi abstract, kita hanya perlu
menambah keyword abstract sebelum penulisan kelas:
1. abstract class Animal {

2.   String name;

3.   int age;

4.   double weight;

5.  

6.   // ...

7. }

Dengan begitu kelas Animal tidak dapat diinisialisasikan menjadi sebuah


objek.
1. var dicodingCat = Animal('Gray', 2, 4.2); // Error: The class 'Animal'

is abstract and can't be instantiated.

Implicit Interface
Selain abstract class, cara lain yang bisa kita gunakan untuk menerapkan
abstraksi dalam OOP adalah dengan interface. Interface atau antarmuka
merupakan set instruksi yang bisa diimplementasi oleh objek. Secara
umum, interface berfungsi sebagai penghubung antara sesuatu yang abstrak
dengan sesuatu yang nyata.

Bayangkan remote TV atau tombol yang ada di HP Anda. Tombol-tombol ini


bisa kita sebut sebagai interface. Kita tak perlu tahu dan peduli tentang
bagaimana fungsi yang ada di dalamnya.

Dart tidak memiliki keyword atau syntax untuk mendeklarasikan interface seperti


bahasa pemrograman OOP lainnya. Setiap class di dalam Dart dapat
bertindak sebagai interface. Maka dari itu interface pada Dart dikenal
sebagai implicit interface. Untuk mengimplementasikan interface,
gunakan keyword implements. Kita bisa mengimplementasikan
beberapa interface sekaligus pada satu kelas.
1. class ClassName implements InterfaceName
Setelah kelas mengimplementasikan interface, maka kelas tersebut wajib
mengimplementasikan semua metode yang ada di dalam interface. Misalnya
kita buat kelas baru bernama Flyable yang akan bertindak sebagai interface.
1. class Flyable {

2.   void fly() { }

3. }

Kita dapat membiarkan body dari method fly() tetap kosong karena fungsi


implementasinya akan dilakukan oleh class. Selanjutnya buat kelas baru
yang mengimplementasi interface Flyable.
1. class Bird extends Animal implements Flyable {

2.   String featherColor;

3.  

4.   Bird(String name, int age, double weight, this.featherColor) :

super(name, age, weight);

5.  

6. }

Anda akan mendapati eror yang memberikan pesan “ Missing concrete


implementation of Flyable.fly ”. Ini artinya kita harus mengimplementasi
fungsi fly yang terdapat pada interface Flyable.
1. class Bird extends Animal implements Flyable {

2.   String featherColor;

3.  

4.   Bird(String name, int age, double weight, this.featherColor) :

super(name, age, weight);

5.  

6.   @override
7.   void fly() {

8.     print('$name is flying');

9.   }

10.  

11. }

Keyword atau anotasi @override menunjukkan fungsi tersebut


mengesampingkan fungsi yang ada di interface atau kelas induknya, lalu
menggunakan fungsi yang ada dalam kelas itu sendiri sebagai gantinya.

Enumerated Types
Bagaimana kita bisa menyimpan banyak nilai konstan di satu tempat dan
menanganinya secara bersamaan? Solusinya, Dart menyediakan
Enumerated Type, sering disebut Enumerations atau Enums. Enums mewakili
kumpulan konstan yang membuat kode kita lebih jelas dan mudah dibaca.
1. enum Rainbow {

2.   red, orange, yellow, green, blue, indigo, violet

3. }

4.  

5. enum Weather {

6.   sunny, cloudy, rain, storm;

7. }

 Enums pada Dart memiliki beberapa properti bawaan yang dapat kita
gunakan untuk menampilkan seluruh nilai dalam bentuk list  serta
menampilkan item dan indeks dari item  tersebut.
1. print(Rainbow.values);

2. print(Rainbow.blue);

3. print(Rainbow.orange.index);
Ketika kode di atas dijalankan, maka konsol akan tampil seperti berikut:
1. [Rainbow.red, Rainbow.orange, Rainbow.yellow, Rainbow.green,

Rainbow.blue, Rainbow.indigo, Rainbow.violet]

2. Rainbow.blue

3. 1

Semenjak Dart versi 2.15, Enums memiliki properti baru untuk menampilkan
nilai String setiap itemnya [3]. Anda dapat gunakan properti .name.
1. print(Rainbow.blue.name);   // blue

Kita juga bisa menggunakan enums ke dalam switch statements. Namun kita


perlu menangani semua kemungkinan nilai enums yang Ada.
1. var weatherForecast = Weather.cloudy;

2.  

3. switch(weatherForecast) {

4.   case Weather.sunny:

5.     print("Today's weather forecast is sunny");

6.     break;

7.   case Weather.cloudy:

8.     print("Today's weather forecast is cloudy");

9.     break;

10.   case Weather.rain:

11.     print("Today's weather forecast is rain");

12.     break;

13.   case Weather.storm:

14.     print("Today's weather forecast is storm");


15.     break;

16. }

Dengan kecanggihan fitur yang disediakan oleh Enums, Dart versi 2.17 rilis
dengan fitur baru [4]. Enums mempunyai fitur layaknya kelas yang
memiliki attribute  dan behaviour. Dengan begitu, kita dapat memberikan
variabel ataupun constructor di dalam Enums.
1. enum Weather {

2.   sunny(15),

3.   cloudy(34),

4.   rain(69),

5.   storm(83);

6.  

7.   final int rainAmount;

8.  

9.   const Weather(this.rainAmount);

10. }

Dengan kehadiran variabel di dalam Enums, kita dapat


akses attribute rainAmount dengan cara seperti berikut.
1. print(Weather.rain.rainAmount);

Selain itu, kita dapat melakukan override method toString(). Dengan begitu,


kita dapat mengonversi teks sesuai dengan apa yang diinginkan.
1. enum Weather {

2.   …

3.  

4.   @override
5. String toString() => "Today's weather forecast is $name with a

$rainAmount% chance of rain";

6. }

Untuk menjalankannya, Anda dapat jalankan perintah berikut.


1. print(Weather.cloudy.toString());

2. // atau

3. print(Weather.cloudy);

Mixins
Mixin adalah cara menggunakan kembali kode kelas dalam banyak hirarki
kelas. Konsep mixin mungkin adalah konsep yang baru bagi Anda karena
konsep ini tidak ada pada bahasa C# atau Java. Jadi kenapa dan kapan kita
perlu menggunakan mixin?

Kita kembali pada contoh hewan. Perhatikan diagram berikut:

Kita memiliki superclass Animal dengan tiga subclass. Di bawahnya ada


beberapa kelas turunan yang memiliki perilaku berbeda-beda. Beberapa
hewan memiliki perilaku yang sama, seperti Cat dan Duck sama-sama bisa
berjalan. Kita bisa saja membuat kelas seperti Walkable, Swimmable,
dan Flyable. Sayangnya, Dart tidak mendukung multiple inheritance, sehingga
sebuah kelas hanya bisa mewarisi (inherit) satu kelas induk. Kita bisa saja
membuat interface lalu mengimplementasikannya ke ke kelas Cat atau Duck.
Namun, implementasi interface mengharuskan kita untuk meng-
override method dan membuat implementasi fungsi di masing-masing kelas.

1. mixin Flyable {

2.   void fly() {

3.     print("I'm flying");

4.   }
5. }

6.  

7. mixin Walkable {

8.   void walk() {

9.     print("I'm walking");

10.   }

11. }

12.  

13. mixin Swimmable {

14.   void swim() {

15.     print("I'm swimming");

16.   }

17. }

Kelas mixin dapat didefinisikan dengan keyword class seperti kelas pada


umumnya. Jika Anda tidak ingin kelasnya bertindak seperti kelas biasa
misalnya seperti bisa diinstansiasi menjadi objek, gunakan
saja keyword mixin. Setelah membuat kelas seperti di atas kita bisa
menambahkan sebagai mixin dengan keyword with dan diikuti dengan satu
atau beberapa kelas mixin.
1. class Cat extends Mammal with Walkable { }

2.  

3. class Duck extends Bird with Walkable, Flyable, Swimmable { }

Dengan mixin ini memungkinkan objek cat untuk memanggil metode walk().


Sementara objek duck bisa memanggil metode walk(), fly(), dan swim().
1. void main() {

2.   var donald = Duck();


3.   var garfield = Cat();

4.  

5.   garfield.walk();

6.  

7.   donald.walk();

8.   donald.swim();

9. }

Jika diperhatikan mixin ini memang mirip dengan multiple inheritance. Namun


kelas mixin ini tidak termasuk ke dalam hirarki parent-child atau inheritance.
Oleh sebab itu mixin memungkinkan kita terhindar dari masalah yang sering
terjadi pada multiple inheritance yang dikenal dengan diamond problem, yaitu
ada dua parent class yang memiliki method dengan nama yang sama
sehingga child class-nya ambigu dalam menjalankan method yang mana.

Sebagai contoh kita punya kelas bernama Performer.


1. abstract class Performer {

2.   void perform();

3. }

Lalu kita punya dua kelas turunan dari Performer.


1. class Dancer extends Performer {

2.   @override

3.   void perform() {

4.     print('Dancing');

5.   }

6. }

7.  

8. class Singer extends Performer {


9.   @override

10.   void perform() {

11.     print('Singing');

12.   }

13. }

Kita asumsikan Dart memiliki dukungan terhadap multiple inheritance sehingga


kita punya 1 kelas lagi seperti berikut:
1. class Musician extends Dancer, Singer {

2.   void showTime() {

3.     perform();

4.   }

5. }

Kira-kira method perform() mana yang akan dijalankan? Beruntung dengan


Dart kita bisa menghindari situasi seperti ini dengan mixin.

Ketika mencampur (mixing) kelas, kelas yang digunakan sebagai mixin tidak


paralel namun saling bertumpuk. Itulah mengapa kelas atau method
pada mixin tidak ambigu satu sama lain. Karena itu jugalah, urutan menjadi
hal yang penting dalam menerapkan mixin. Misalnya kita telah
menerapkan mixin pada kelas Musician.
1. mixin Dancer implements Performer {

2.   @override

3.   void perform() {

4.     print('Dancing');

5.   }

6. }

7.  
8. mixin Singer implements Performer {

9.   @override

10.   void perform() {

11.     print('Singing');

12.   }

13. }

14.  

15. class Musician extends Performer with Dancer, Singer {

16.   void showTime() {

17.     perform();

18.   }

19. }

Lalu buatlah objek yang akan menjalankan method perform().


1. void main() {

2.   var arielNoah = Musician();

3.   arielNoah.perform();

4. }

Coba jalankan fungsi main di atas, apakah yang akan tampil pada konsol?
Mengapa demikian? Seperti yang telah dijelaskan,
kelas mixin bersifat stack atau bertumpuk. Kelas-kelas ini berurutan dari yang
paling umum hingga paling spesifik. Sehingga sesuai urutan mixin di atas
kelas Musician akan menampilkan method dari Singer karena berada di
urutan terakhir atau paling spesifik.
Extension Methods
Pada versi 2.7 Dart mengenalkan fitur baru yaitu extension methods.
Tujuan dari fitur ini adalah supaya kita bisa membuat fungsionalitas
tambahan dari library yang sudah ada.

Ketika Anda menggunakan library, baik itu library bawaan Dart atau


pun library milik orang lain, ada kemungkinan library tersebut kurang lengkap
sehingga kita perlu menambahkan beberapa fungsionalitas. Namun akan jadi
PR kita untuk mengubah library yang sudah ada. Dengan extension method,
kita dapat membuat fungsi atau method tambahan lalu menggunakannya
sesuai dengan kebutuhan aplikasi kita.

Contoh sederhananya, kita memiliki variabel list integer.


1. var unsortedNumbers = [2, 5, 3, 1, 4];

Kita memiliki kebutuhan untuk mengurutkan nilai di dalam list tersebut


namun List pada Dart tidak memiliki fungsi untuk mengurutkannya (Dart
memiliki fungsi sort() namun bersifat mentransformasi list dan tidak
mengembalikan nilai). Kita bisa membuat extension method dari
objek List dengan sintaks seperti berikut:
1. /* extension <extension name> on <type> {

2.   (<member definition>)*

3. } */

4.  

5. extension Sorting on List<int> {

6.   List<int> sortAsc() {

7.     var list = this;

8.     var length = this.length;

9.  

10.  

11.     for (int i = 0; i < length - 1; i++) {

12.       int min = i;


13.       for (int j = i + 1; j < length; j++) {

14.         if (list[j] < list[min]) {

15.           min = j;

16.         }

17.       }

18.  

19.  

20.       int tmp = list[min];

21.       list[min] = list[i];

22.       list[i] = tmp;

23.     }

24.  

25.  

26.     return list;

27.   }

28. }

Pada extension method di atas kita telah membuat method atau fungsi untuk


mengurutkan data di dalam list menggunakan selection sort algorithm.
Selanjutnya kita bisa memanggil method ini dari objek list.
1. void main() {

2.   var unsortedNumbers = [2, 5, 3, 1, 4];

3.   print(unsortedNumbers);

4.   var sortedNumbers = unsortedNumbers.sortAsc();

5.   print(sortedNumbers);

6.  
7.  

8.   /*

9.   Output: [2, 5, 3, 1, 4]

10.           [1, 2, 3, 4, 5]

11.    */

12. }

Kita juga bisa menggunakan kembali extension method ini di beberapa berkas


yang berbeda sebagai library.

 main.dart
 extensions.dart
1. import 'extension.dart';

2.  

3. void main() {

4.   var unsortedNumbers = [2, 5, 3, 1, 4];

5.   print(unsortedNumbers);

6.   var sortedNumbers = unsortedNumbers.sortAsc();

7.   print(sortedNumbers);

8. }

Rangkuman Materi
Kita telah berada di akhir dari modul Object Oriented Programming. Mari kita
uraikan materi yang sudah Anda pelajari.

 OOP merupakan paradigma yang berdasarkan pada konsep objek


yang memiliki atribut serta dapat melakukan operasi atau prosedur
tertentu.
 Terdapat 4 (empat) pilar dalam object oriented programming, antara
lain:
o Encapsulation, kondisi di mana status atau kondisi di dalam
class, dibungkus, dan bersifat privat. Artinya objek lain tidak bisa
mengakses property secara langsung.
o Abstraction, objek hanya menunjukkan operasinya secara high-
level. Cukup tau method apa saja yang dibuat oleh class
tersebut.
o Inheritance, mewarisi properti kelas induk (parent class) ke kelas
anak (child class). 
o Polymorphism, objek class dapat memiliki bentuk atau
implementasi yang berbeda-beda pada satu metode yang sama.
 Class merupakan blueprint untuk mendefinisikan karakteristik dari
sebuah objek.
 Di dalam class, variabel berlaku sebagai properti, sementara fungsi
sebagai method.
 Abstract Class merupakan gambaran umum dari sebuah kelas.
Abstract class tidak dapat direalisasikan ke dalam sebuah objek.
 Mixin digunakan untuk menggabungkan dan memanfaatkan
property/method dari beberapa kelas lain.
 Extension methods digunakan untuk membuat method tambahan pada
kelas yang sudah ada.
Paradigma Functional Programming
Seperti yang sudah disampaikan di awal, Dart adalah bahasa yang
mendukung multiparadigm. Artinya selain merupakan bahasa pemrograman
berorientasi objek, penulisan syntax Dart juga menggunakan gaya functional
programming.

Functional programming adalah paradigma pemrograman di mana proses


komputasi didasarkan pada fungsi matematika murni. Functional
programming (FP) ditulis dengan gaya deklaratif yang berfokus pada “ what to
solve” dibandingkan “how to solve” pada gaya imperatif.

Berikut ini beberapa konsep dan karakteristik functional programming:

Pure functions

Pure functions berarti sebuah fungsi bergantung pada argumen atau


parameter yang dimasukkan ke dalamnya. Sehingga pemanggilan fungsi
dengan nilai argumen yang sama akan selalu memberikan hasil yang sama
pula. Contohnya pada fungsi sum() berikut nilai yang dikembalikan akan
bergantung pada argumen yang diberikan.
1. int sum(int num1, int num2) {

2.   return num1 + num2;

3. }

Recursion

Pada functional programming tidak ada konsep perulangan


seperti for atau while. Iterasi pada functional programming dilakukan melalui
rekursi atau pemanggilan fungsi dari fungsi itu sendiri, hingga mencapai
kasus dasar.
1. int fibonacci(n) {

2.   if (n <= 0) {

3.     return 0;

4.   } else if(n == 1) {

5.     return 1;
6.   } else {

7.     return fibonacci(n - 1) + fibonacci(n - 2);

8.   }

9. }

Immutable variables

Variabel pada functional programming bersifat immutable, artinya kita tidak bisa


mengubah sebuah variabel ketika sudah diinisialisasi. Alih-alih mengubah
nilai variabel, kita bisa membuat variabel baru untuk menyimpan data.
Mekanisme ini bertujuan agar kode kita menjadi lebih aman karena state dari
aplikasi tidak akan berubah sepanjang aplikasi berjalan.
1. var x = 5;

2. x = x + 1; // Contoh variable yang tidak immutable

Functions are first-class citizen and can be higher-order

Maksud dari function merupakan first-class citizen adalah


bahwa function berlaku sama seperti komponen pemrograman yang lain.
Sebuah fungsi bisa dimasukkan ke variabel menjadi parameter dalam suatu
fungsi dan juga menjadi nilai kembalian pada fungsi. Higher order
functions adalah fungsi yang mengambil fungsi lain sebagai argumen dan juga
dapat mengembalikan fungsi.

Pada modul ini kita akan mempelajari bagaimana penulisan


gaya functional dengan bahasa Dart.

Anonymous Functions
Masih ingatkah Anda dengan materi function dan cara membuatnya? Seperti
yang kita tahu, untuk mendeklarasikan sebuah fungsi kita perlu
mendefinisikan nilai kembalian dan juga nama fungsinya.
1. int sum(int num1, int num2) {

2.   return num1 + num2;

3. }
Kebanyakan fungsi pada Dart memiliki nama seperti sum(), main(),
atau print(). Pada Dart kita bisa membuat fungsi yang tidak bernama
alias nameless atau anonymous. Anonymous function ini juga dikenal dengan
nama lambda.

Untuk membuat lambda atau anonymous function kita cukup menuliskan tanda


kurung untuk menerima parameter dan body function-nya.
1. void main() {

2.   (int num1, int num2) {

3.     return num1 + num2;

4.   };

5. }

Lalu bagaimana kita bisa menggunakan fungsi tersebut? Seperti yang telah
dijelaskan sebelumnya bahwa function adalah first-class citizen, maka fungsi
juga merupakan sebuah objek yang bisa disimpan ke dalam variabel. Kita
bisa menggunakan keyword var atau secara eksplisit menggunakan tipe
data Function.
1. void main() {

2.   var sum = (int num1, int num2) {

3.     return num1 + num2;

4.   };

5.  

6.   Function printLambda = () {

7.     print('This is lambda function');

8.   };

9. }

Untuk memanggilnya kita bisa langsung memanggil nama variabelnya seperti


berikut:
1. printLambda();

2. print(sum(3, 4));

Selain itu lambda juga mendukung function expression untuk membuat kode


fungsi menjadi lebih ringkas dengan memanfaatkan fat arrow (=>).
1. var sum = (int num1, int num2) => num1 + num2;

2. Function printLambda = () => print('This is lambda function');

Higher-Order Functions
Setelah mempelajari modul sebelumnya, Anda mungkin bertanya apa yang
bisa dilakukan dengan lambda atau anonymous function?

Kita bisa memanfaatkan lambda untuk membuat higher-order function. Higher


order function adalah fungsi yang menggunakan fungsi lainnya sebagai
parameter, menjadi tipe kembalian, atau keduanya.

Coba perhatikan fungsi berikut:


1. void myHigherOrderFunction(String message, Function myFunction) {

2.   print(message);

3.   print(myFunction(3, 4));

4. }

Fungsi di atas merupakan higher order function karena menerima parameter


berupa fungsi lain. Untuk memanggil fungsi di atas, kita bisa langsung
memasukkan lambda sebagai parameter maupun variabel yang berisi nilai
berupa fungsi.
1. // Opsi 1

2. Function sum = (int num1, int num2) => num1 + num2;

3. myHigherOrderFunction('Hello', sum);

4.  
5.  

6. // Opsi 2

7. myHigherOrderFunction('Hello', (num1, num2) => num1 + num2);

Jika disimulasikan fungsi myHigherOrderFunction akan memanggil


fungsi sum yang dijadikan parameter.
1. void myHigherOrderFunction(String message, Function myFunction) {

2.   print(message);

3.   print(myFunction(3, 4));  // sum(3, 4)    // return 3 + 4

4. }

Namun deklarasi higher order function ini bisa menjadi sedikit tricky. Misalnya


kode di bawah ini tidak akan terdeteksi eror namun ketika dijalankan, aplikasi
Anda akan mengalami crash. Tahukah kenapa?
1. void myHigherOrderFunction(String message, Function myFunction) {

2.   print(message);

3.   print(myFunction(4));

4. }

Karena kita tidak menentukan spesifikasi dari fungsi seperti jumlah parameter
atau nilai kembaliannya, maka semua jenis fungsi akan bisa dijalankan
termasuk pemanggilan myFunction seperti di atas. Untuk mengatasinya kita
bisa lebih spesifik menentukan seperti apa fungsi yang valid untuk menjadi
parameter.
1. void myHigherOrderFunction(String message, int Function(int num1, int

num2) myFunction) { }

Pada fungsi di atas kita perlu memasukkan fungsi dengan dua parameter dan
nilai kembali berupa int sebagai parameter.
Pada materi collection sebenarnya kita telah menggunakan satu fungsi yang
merupakan higher order function yaitu fungsi forEach(). Sebagai contoh kita
punya daftar bilangan fibonacci yang disimpan ke sebuah variabel.
1. var fibonacci = [0, 1, 1, 2, 3, 5, 8, 13];

IntelliJ IDEA akan menunjukkan suggestion apa saja yang perlu menjadi


parameter. Kita bisa melihat bahwa forEach membutuhkan satu parameter
berupa fungsi.

Sehingga ketika memanggil fungsi ini kita bisa melakukan operasi pada
masing-masing item misalnya mencetak ke konsol.
1. fibonacci.forEach((item) {

2.   print(item);

3. });

Closures
Suatu fungsi dapat dibuat dalam lingkup global atau di dalam fungsi lain.
Suatu fungsi yang dapat mengakses variabel di dalam lexical scope-nya
disebut dengan closure. Lexical scope berarti bahwa pada sebuah fungsi
bersarang (nested functions), fungsi yang berada di dalam memiliki akses ke
variabel di lingkup induknya.

Berikut ini adalah contoh kode implementasi closure:


1. void main() {

2.   var closureExample = calculate(2);

3.   closureExample();

4.   closureExample();

5. }
6.  

7. Function calculate(base) {

8.   var count = 1;

9.  

10.   return () => print("Value is ${base + count++}");

11. }

Ketika kode di atas dijalankan, konsol akan tampil seperti berikut:


1. Value is 3

2. Value is 4

Di dalam fungsi calculate() terdapat variabel count dan mengembalikan nilai


berupa fungsi. Fungsi lambda di dalamnya memiliki akses ke
variabel count karena berada pada lingkup yang sama. Karena
variabel count berada pada scope calculate, maka umumnya variabel tersebut
akan hilang atau dihapus ketika fungsinya selesai dijalankan. Namun pada
kasus di atas fungsi lambda atau closureExample masih memiliki referensi
atau akses ke variabel count sehingga bisa diubah. Variabel pada
mekanisme di atas telah tertutup (close covered), yang berarti variabel
tersebut berada di dalam closure.

Rangkuman Materi
Anda berada di akhir dari modul Functional Programming. Mari kita uraikan
materi yang sudah Anda pelajari untuk mempertajam pemahaman.

 Functional Programming (FP) merupakan paradigma pemrograman di


mana proses komputasi didasarkan pada fungsi matematika murni.
 Functional programming ditulis dengan gaya deklaratif yang berfokus
pada “what to solve” dibandingkan “how to solve” pada gaya imperatif.
 Terdapat beberapa konsep dan karakteristik functional programming,
antara lain:
o Pure functions, fungsi bergantung pada argumen dan parameter
yang dimasukkan ke dalamnya.
o Recursion, fungsi yang tidak memiliki konsep perulangan
sehingga fungsi hanya berfokus pada perintah dasar.
o Immutable variables, fungsi bersifat immutable, artinya tidak bisa
mengubah sebuah variabel ketika sudah diinisialisasi.
o First-class citizen, fungsi memiliki sifat yang setara dengan
komponen pemrograman yang lain.
o Higher-order function, fungsi yang menggunakan fungsi lain
sebagai argumen dan juga dapat mengembalikan fungsi.
 Anonymous function atau lambda adalah fungsi yang tidak memiliki
nama.
 Closure merupakan suatu fungsi yang dapat mengakses variabel di
dalam lexical scope-nya.

Dengan ringkasan tersebut, diharapkan Anda dapat memahami semua


materi yang telah disampaikan. Jika belum, Anda bisa ulas kembali materi
yang diberikan pada modul ini. Untuk Anda yang sudah merasa mantap, yuk
lanjut ke modul berikutnya!

Pengenalan Dart Type System


Dalam bahasa pemrograman, type system adalah sistem logis yang terdiri dari
seperangkat aturan yang menetapkan properti atau tipe ke berbagai
konstruksi program komputer, seperti variabel, expression, fungsi, atau
modul. Type system ini memformalkan atau memberikan standar kategori
tersirat yang digunakan programmer untuk tipe data, struktur data, atau
komponen lainnya.

Dart menyebut type system-nya sebagai sound type system. Soundness ini


berarti program Anda tidak akan pernah bisa memasuki keadaan di mana
sebuah ekspresi mengevaluasi nilai yang tidak cocok dengan jenis tipenya.

Sound type system pada Dart ini sama dengan type system pada Java atau C#.
Di mana kondisi soundness ini dicapai dengan menggunakan kombinasi
pemeriksaan statis (compile-time error) dan pemeriksaan saat runtime. Sebagai
contoh, menetapkan String ke variabel int adalah kesalahan compile-time.
Casting Object ke String dengan as String akan gagal ketika runtime jika objek
tersebut bukan String.

Manfaat dari sound type system ini, antara lain:


 Mengungkap bug terkait tipe pada saat compile time.
Sound type system memaksa kode untuk tidak ambigu tentang tipenya,
sehingga bug terkait tipe yang mungkin sulit ditemukan saat runtime, bisa
ditemukan pada waktu kompilasi.
 Kode lebih mudah dibaca.
Kode menjadi lebih mudah dibaca karena Anda dapat mengandalkan
nilai yang benar-benar memiliki tipe yang ditentukan. Tipe pada Dart
tidak bisa berbohong.
 Kode lebih mudah dikelola.
Ketika Anda mengubah satu bagian kode, type system dapat
memperingatkan Anda tentang bagian kode mana yang baru saja
rusak.
 Kompilasi ahead of time (AOT) yang lebih baik.
Kode yang dihasilkan saat kompilasi AOT menjadi jauh lebih efisien.

Generic
Jika Anda perhatikan pada dokumentasi collection seperti List, sebenarnya
tipe dari List tersebut adalah List<E>. Tanda <...> ini menunjukkan
bahwa List adalah tipe generic, tipe yang memiliki tipe parameter.
Menurut coding convention dari Dart, tipe parameter dilambangkan dengan
satu huruf kapital seperti E, T, K, atau V.

Secara umum generic merupakan konsep yang digunakan untuk menentukan


tipe data yang akan kita gunakan. Kita bisa mengganti tipe
parameter generic pada Dart dengan tipe yang lebih spesifik dengan
menentukan instance dari tipe tersebut.

Sebagai contoh, perhatikan List yang menyimpan beberapa nilai berikut:


1. List<int> numberList = [1, 2, 3, 4, 5];

Tipe parameter yang digunakan pada variabel list di atas adalah int, maka
nilai yang bisa kita masukkan adalah nilai dengan tipe int. Begitu juga jika kita
menentukan tipe parameter String, maka tipe yang bisa kita masukkan ke
dalam list hanya berupa String.
1. List<int> numberList = [1, 2, 3, 4, 5];

2. List<String> stringList = ['Dart', 'Flutter', 'Android', 'iOS'];


3. List dynamicList = [1, 2, 3, 'empat'];  // List<dynamic>

Berbeda jika kita tidak menentukan tipe parameter dari list. List tersebut tidak


memiliki tipe yang menjadi acuan bagi kompiler sehingga semua tipe bisa
disimpan ke dalam list. Variabel dynamicList di atas sebenarnya masih
menerapkan generic dengan tipe dynamic sehingga tipenya
menjadi List<dynamic>.

Dari kasus di atas kita bisa simpulkan bahwa Dart membantu kita
menghasilkan kode yang type safe dengan membatasi tipe yang bisa
digunakan ke dalam suatu objek dan menghindari bug. Selain itu generic juga
bermanfaat mengurangi duplikasi kode. Misalnya ketika Anda perlu untuk
menyimpan objek cache bertipe String dan int. Alih-alih membuat dua
objek StringCache dan IntCache, Anda bisa membuat satu objek saja
dengan memanfaatkan tipe parameter dari generic.
1. abstract class Cache<T> {

2.   T getByKey(String key);

3.   void setByKey(String key, T value);

4. }

Dengan Dart type system kita bisa mengganti tipe parameter yang digunakan


sesuai dengan susunan hierarkinya. Perhatikan hierarki objek Animal berikut:

Dengan hierarki di atas, jika kita memiliki objek List<Bird> maka objek apa


saja yang bisa kita masukkan ke list tersebut?
1. List<Bird> birdList = [Bird(), Dove(), Duck()];

Seluruh objek Bird atau objek turunannya bisa masuk ke dalam birdList.


Namun, ketika menambahkan objek dari Animal, terjadi compile error karena
objek Animal belum tentu merupakan objek Bird.
1. List<Bird> birdList = [Bird(), Dove(), Duck(), Animal()];  // Error
Berbeda jika kita mengisi List<Bird> dengan List<Animal> seperti berikut:
1. List<Bird> myBird = List<Animal>();

Kompiler tidak akan menunjukkan eror namun ketika kode dijalankan akan
terjadi runtime error karena List<Animal> bukanlah subtype dari List<BIrd>.
1. Unhandled exception:

2. type 'List<Animal>' is not a subtype of type 'List<Bird>'

Type Inference
Seperti yang kita tahu Dart mendukung type inference. Dart
memiliki analyzer yang dapat menentukan menyimpulkan tipe
untuk field, method, variabel lokal, dan beberapa tipe argumen generic.
Ketika analyzer tidak memiliki informasi yang cukup untuk menyimpulkan tipe
tertentu, maka tipe dynamic akan digunakan.

Misalnya berikut ini adalah contoh penulisan variabel map dengan tipe yang


eksplisit:
1. Map<String, dynamic> company = {'name': 'Dicoding', 'yearsFounded':

2015};

Atau, Anda dapat menggunakan var dan Dart akan menentukan tipenya.


1. var company = {'name': 'Dicoding', 'yearsFounded': 2015}; //

Map<String, Object>

Type inference menentukan tipe dari entri kemudian menentukan tipe dari
variabelnya. Pada contoh di atas, kedua key dari map adalah String, namun
nilainya memiliki tipe yang berbeda, yaitu String dan int, di mana keduanya
merupakan turunan dari Object. Sehingga variabel company akan memiliki
tipe Map<String, Object>.

Saat menetapkan nilai objek ke dalam objek lain, kita bisa mengganti tipenya
dengan tipe yang berbeda tergantung pada apakah objek tersebut
adalah consumer atau producer. Perhatikan assignment berikut:
1. Fish fish = Fish();

Fish fish adalah consumer dan Fish() adalah producer. Pada posisi consumer,


aman untuk mengganti consumer bertipe yang spesifik dengan tipe yang lebih
umum. Jadi, aman untuk mengganti Fish fish dengan Animal
fish karena Animal adalah supertype dari Fish.
1. Animal fish = Fish();

Namun mengganti Fish fish dengan Shark fish melanggar type safety karena


bisa saja Fish memiliki subtype lain dengan perilaku berbeda,
misalnya FlyingFish.
1. Shark fish = Fish();  // Error

Pada posisi producer, aman untuk mengganti tipe yang umum (supertype)


dengan tipe yang lebih spesifik (subtype).
1. Fish fish = Shark();

Rangkuman Materi
Anda berada di akhir dari modul Dart Type System. Mari kita uraikan materi
yang sudah Anda pelajari untuk mempertajam pemahaman.

 Type system merupakan seperangkat aturan yang menetapkan properti


atau tipe ke berbagai konstruksi program komputer, seperti
variable, expression, fungsi, atau modul.
 Type system ini memberikan standar kategori tersirat yang digunakan
programmer untuk tipe data, struktur data, atau komponen lainnya.
 Pada Dart memiliki type system-nya dengan sound type system dengan
maksud program akan selalu mengevaluasi nilai yang tidak cocok
dengan jenis tipenya.
 Manfaat adanya sound type system pada Dart adalah:
o Menemukan bug  terkait tipe data pada saat   compile time.
o Kode lebih mudah dibaca.
o Kode lebih mudah dikelola.
o Kompilasi ahead of time (AOT) yang lebih baik.
 Pengertian Generic secara umum adalah konsep yang digunakan
untuk menentukan tipe data yang akan digunakan.
 Manfaat Generic pada Dart adalah:
o Type safety, maksudnya objek akan membatasi tipe yang bisa
digunakan ke dalam objek tersebut.
o Mengurangi duplikasi kode sehingga dapat mempermudah dalam
membuat interface  baru.
 Dart mendukung type inference, di mana memiliki analyzer yang dapat
menyimpulkan tipe untuk field, method, variabel lokal, dan beberapa tipe
argumen generic  lainnya.

Mari kita lanjut ke modul berikutnya untuk mempelajari tentang proses


asynchronous pada Dart. Let’s go!

Anda mungkin juga menyukai