Dart Sealed Classes

sealed class adalah fitur baru yang diperkenalkan di Dart 3. Secara sederhana, sealed class adalah sebuah kelas abstrak yang memiliki himpunan subclass yang telah ditentukan, dan semua subclass tersebut harus dideklarasikan di dalam library (file .dart) yang sama dengan sealed class itu sendiri.

Tujuan utama dari sealed class adalah untuk membuat hierarki kelas yang “tertutup” atau “terbatas”. Artinya, kita hanya bisa membuat subclass dari kelas sealed di lokasi yang sama dengan kelas tersebut. Ini mencegah developer lain (terutama dari package lain) membuat subclass baru dari kelas sealed Anda.

Kapan menggunakan sealed class? Anda harus menggunakan sealed class saat kita memiliki kelas yang merepresentasikan status atau varian yang terbatas dan saling eksklusif. Contoh klasik adalah status koneksi jaringan atau hasil dari sebuah operasi asinkron.

Keunggulan Utama sealed class

  1. Kontrol Hierarki: kita memegang kendali penuh atas subclass yang diizinkan, mencegah penambahan subclass yang tidak terduga di luar library Anda.
  2. Pengecekan Komprehensif (Exhaustive Checking): Ini adalah fitur yang paling powerful dari sealed class. Ketika kita menggunakan switch atau if pada objek sealed class, Dart akan secara otomatis memeriksa apakah kita telah menangani semua kemungkinan subclass. Jika ada yang terlewat, analyser (bagian dari IDE yang memeriksa kode) akan memberikan peringatan atau error. Ini menjamin kode kita lebih aman dan tidak akan gagal karena kasus yang tidak terduga.
  3. Tidak Perlu Klausa default: Karena Dart tahu bahwa semua subclass sudah tercakup, kita tidak perlu lagi menggunakan klausa default pada switch. Ini membuat kode lebih bersih dan lebih aman.

Contoh Penggunaan Sealed Class

sealed class Vehicle {
  void startEngine() {
    print("Engine started");
  }

  void stopEngine() {
    print("Engine stopped");
  }
}

// Hanya class di file ini yang boleh extends/implements/mixin Vehicle
class Car extends Vehicle {
  @override
  void startEngine() {
    print("Car engine started");
  }
}

class Bike extends Vehicle {
  @override
  void startEngine() {
    print("Bike engine started");
  }
}

void main() {
  var myCar = Car();
  myCar.startEngine();
  myCar.stopEngine();

  var myBike = Bike();
  myBike.startEngine();
  myBike.stopEngine();
}

Jika kita jalankan maka akan seperti berikut

$ dart run SealedClass.dart 
Car engine started
Engine stopped
Bike engine started
Engine stopped

Perbedaan dengan Final dan Interface Class

  • final class: Tidak bisa diwarisi sama sekali.
  • sealed class: Hanya bisa diwarisi di file yang sama.
  • interface class: Hanya bisa diimplementasikan, tidak bisa diinstansiasi langsung.

Sample source code bisa di download di github pada link berikut dart-tutorial

hyvercode

Dart Final Classes

final class adalah fitur di Dart yang digunakan untuk mencegah class tersebut diwarisi (extends) atau diimplementasikan (implements) oleh class lain. Artinya, class yang dideklarasikan dengan kata kunci final hanya bisa digunakan secara langsung, tidak bisa menjadi parent class

Buat file dengan nama FinalClass.dart

final class vehile {
  void startEngine() {
    print("Engine started");
  }

  void stopEngine() {
    print("Engine stopped");
  }
}

// Extending the final class error canont be extended
class Car extends vehile {
  @override
  void startEngine() {
    super.startEngine();
  }

  @override
  void stopEngine() {
    super.stopEngine();
  }
}

// Impelement the final class error canont be extended
class Bike implements vehile {
  @override
  void startEngine() {
    print("Bike engine started");
  }

  @override
  void stopEngine() {
    print("Bike engine stopped");
  }
}

void main() {
  var myCar = Car();
  myCar.startEngine();
  myCar.stopEngine();

  var myBike = Bike();
  myBike.startEngine();
  myBike.stopEngine();
}

Ketika kita jalankan maka akan terjadi error seperti berikut :

$ dart run FinalClass.dart 
FinalClass.dart:12:7: Error: The type 'Car' must be 'base', 'final' or 'sealed' because the supertype 'vehile' is 'final'.
Try adding 'base', 'final', or 'sealed' to the type.
class Car extends vehile {
      ^
FinalClass.dart:25:7: Error: The type 'Bike' must be 'base', 'final' or 'sealed' because the supertype 'vehile' is 'final'.
Try adding 'base', 'final', or 'sealed' to the type.
class Bike implements vehile {

Penjelasan Kode: Interface Class di Dart

Pada contoh di atas, vehile adalah sebuah final class.

Kenapa Terjadi Error?

1. Mencoba Extends Final Class, Kode di atas akan error karena vehile adalah final class, sehingga tidak boleh diwarisi oleh class lain.

2. Mencoba Implements Final Class, Begitu juga saat mencoba mengimplementasikan final class.

Tujuan Final Class

  • Keamanan kode: Mencegah perubahan perilaku class yang tidak diinginkan melalui pewarisan.
  • Optimasi: Compiler bisa melakukan optimasi lebih baik karena tahu class tersebut tidak akan diwarisi.
  • Desain API: Cocok untuk class yang memang tidak boleh diubah perilakunya oleh developer lain.

Kesimpulan

  • Gunakan final class jika ingin membuat class yang tidak boleh diwarisi atau diimplementasikan.
  • Jika mencoba mewarisi atau mengimplementasikan final class, akan muncul error dari Dart.

Untuk memanggil atau menggunakan final class di Dart, kita tidak bisa mewarisi (extends) atau mengimplementasikan (implements) class tersebut.
Namun, kita tetap bisa membuat objek langsung dari final class dan memanggil method-method yang ada di dalamnya.

Contoh Penggunaan Final Class

final class Vehicle {
  void startEngine() {
    print("Engine started");
  }

  void stopEngine() {
    print("Engine stopped");
  }
}

void main() {
  var myVehicle = Vehicle(); // Membuat objek langsung dari final class
  myVehicle.startEngine();   // Memanggil method
  myVehicle.stopEngine();
}

Sample source code bisa di download di github pada link berikut dart-tutorial

hyvercode

Dart Interface Classes

Dalam Dart, untuk mebuat calss interface dalam dart kita bisa mengunakan kata kunci interface. Meskipun pada dart, setiap class secara implisit adalah sebuah interface. Ini berarti kita dapat menggunakan kata kunci implements pada sebuah class untuk mengimplementasikan interface dari class lain.

  • Ketika sebuah class mengimplementasikan sebuah interface, ia harus menyediakan implementasi (body) untuk semua properti dan method dari interface tersebut.
  • Ini berguna untuk memastikan sebuah class memenuhi “kontrak” tertentu, tanpa perlu mewarisi implementasinya.

Buat file dengan nama InterfaceClass.dart

interface class vehicle {
  void startEngine() {
    print("Engine started");
  }

  void stopEngine() {
    print("Engine stopped");
  }
}

// Extending the interface class
class Car extends vehicle {
  @override
  void startEngine() {
    super.startEngine();
  }

  @override
  void stopEngine() {
    super.stopEngine();
  }
}

// Another class implementing the interface class
class Bike implements vehicle {
  @override
  void startEngine() {
    print("Bike engine started");
  }

  @override
  void stopEngine() {
    print("Bike engine stopped");
  }
}

// Main function to demonstrate the usage
void main() {
  print("Interface Class Extends:");
  var myCar = Car();
  myCar.startEngine();
  myCar.stopEngine();

  print("Interface Class Implements:");
  var myBike = Bike();
  myBike.startEngine();
  myBike.stopEngine();
}

Contoh Output:

$ dart run InterfaceClass.dart
Interface Class Extends:
Engine started
Engine stopped
Interface Class Implements:
Bike engine started
Bike engine stopped

Penjelasan Kode: Interface Class di Dart

1. Interface Class vehicle

interface class vehicle {
  void startEngine() {
    print("Engine started");
  }

  void stopEngine() {
    print("Engine stopped");
  }
}
  • interface class adalah fitur baru di Dart yang memungkinkan sebuah class hanya bisa di-extend atau di-implement oleh class lain, tapi tidak bisa di-instansiasi langsung.
  • Pada contoh di atas, vehicle mendefinisikan dua method: startEngine() dan stopEngine(), yang masing-masing mencetak pesan ke konsol.

2. Extending Interface Class: Car

class Car extends vehicle {
  @override
  void startEngine() {
    super.startEngine();
  }

  @override
  void stopEngine() {
    super.stopEngine();
  }
}
  • Car adalah class yang mewarisi (extends) dari vehicle.
  • Method startEngine() dan stopEngine() di-override untuk menunjukkan bahwa class turunan bisa mengubah atau menambah perilaku dari method parent-nya.
  • Pada contoh ini, method hanya memanggil method dari parent (super.startEngine()), tapi bisa dikembangkan sesuai kebutuhan.

3. Implementasi Lain: Bike

class Bike extends vehicle {
  @override
  void startEngine() {
    super.startEngine();
  }

  @override
  void stopEngine() {
    super.stopEngine();
  }
}

Bike juga mewarisi dari vehicle dan meng-override kedua method dengan cara yang sama seperti Car.

4. Fungsi Main

void main() {
  var myCar = Car();
  myCar.startEngine();
  myCar.stopEngine();

  var myBike = Bike();
  myBike.startEngine();
  myBike.stopEngine();
}
  • Pada fungsi main, objek Car dan Bike dibuat.
  • Setiap objek memanggil method startEngine() dan stopEngine(), sehingga akan mencetak pesan ke konsol.

Kesimpulan

  • interface class di Dart sangat berguna untuk membuat kontrak perilaku yang harus diikuti oleh class turunan.
  • Tidak bisa membuat objek langsung dari vehicle, tapi bisa membuat objek dari class turunannya (CarBike).
  • Cocok digunakan untuk arsitektur aplikasi yang membutuhkan konsistensi perilaku pada beberapa class turunan.

Sample source code bisa di download di github pada link berikut dart-tutorial

hyvercode