Fungsi dan closure

Menulis fungsi Dart dengan sintaks panah, parameter opsional, parameter bernama, dan closure.

Daftar isi

Fungsi adalah unit dasar penyusunan perilaku di Dart, dan Flutter menggunakannya secara intensif, mulai dari parameter bernama pada konfigurasi widget hingga callback, yaitu fungsi yang diteruskan agar dipanggil belakangan ketika keadaan tertentu terjadi. Topik ini membahas deklarasi fungsi, sintaks panah, parameter posisi opsional, parameter bernama dengan required dan nilai bawaan, fungsi sebagai nilai, serta closure yang menangkap variabel lingkup luarnya. Setelah membacanya, fungsi untuk berbagai kebutuhan dapat ditulis dengan bentuk parameter yang tepat.

Program pertama: fungsi untuk keperluan kelas#

Program berikut menghimpun empat bentuk fungsi yang lazim, dari label dengan gelar opsional hingga penghitung berbasis closure. Seluruh fungsi pada contoh ditulis di tingkat atas berkas, tanpa melekat pada kelas mana pun. Simpan program berikut sebagai berkas closure.dart, lalu jalankan perintah dart closure.dart dari direktori tempat berkas itu berada.

dart closure.dart
// Demonstrasi fungsi: parameter opsional, parameter bernama, dan closure.

// Parameter posisi opsional dalam kurung siku, dengan nilai bawaan.
String label(String nama, [String gelar = '']) {
  if (gelar.isEmpty) {
    return nama;
  }
  return '$gelar $nama';
}

// Parameter bernama: acara wajib diisi (required), sapaan punya nilai bawaan.
String sapa(String nama, {required String acara, String sapaan = 'Selamat datang'}) {
  return '$sapaan, $nama, di acara $acara.';
}

// Sintaks panah: tubuh fungsi hanya satu ekspresi.
double kaliDua(double angka) => angka * 2;

// Closure: fungsi yang dikembalikan menangkap variabel hitungan.
int Function() buatPenghitung() {
  int hitungan = 0;
  return () {
    hitungan += 1;
    return hitungan;
  };
}

void main() {
  print(label('Citra'));
  print(label('Budi', 'Ketua Kelas'));

  print(sapa('Dewi', acara: 'Rapat Kelas'));
  print(sapa('Ali', acara: 'Rapat Kelas', sapaan: 'Selamat bergabung'));

  print('kaliDua(21) = ${kaliDua(21)}');

  // Fungsi sebagai nilai: disimpan di variabel, lalu dipanggil.
  var operasi = kaliDua;
  print('operasi(10) = ${operasi(10)}');

  // Dua closure berdiri sendiri, masing-masing membawa hitungannya.
  var penghitungA = buatPenghitung();
  var penghitungB = buatPenghitung();
  print('A: ${penghitungA()}, ${penghitungA()}, ${penghitungA()}');
  print('B: ${penghitungB()}');
  print('A lagi: ${penghitungA()}');
}
text Keluaran
Citra
Ketua Kelas Budi
Selamat datang, Dewi, di acara Rapat Kelas.
Selamat bergabung, Ali, di acara Rapat Kelas.
kaliDua(21) = 42.0
operasi(10) = 20.0
A: 1, 2, 3
B: 1
A lagi: 4

Empat baris pertama keluaran berasal dari fungsi label() dan sapa() yang mendemonstrasikan parameter opsional. Baris kaliDua(21) = 42.0 memperlihatkan sintaks panah, sedangkan tiga baris terakhir membuktikan bahwa dua closure yang dibuat dari fungsi yang sama menyimpan hitungan yang berdiri sendiri. penghitungA mencatat 1, 2, 3, kemudian penghitungB memulai dari 1 lagi, dan pemanggilan penghitungA() berikutnya berlanjut ke 4. Bagian berikut menguraikan setiap bentuknya satu per satu.

Parameter opsional dan parameter bernama#

Dart menyediakan dua jenis parameter opsional. Parameter posisi opsional ditulis dalam kurung siku setelah parameter wajib, sebagaimana gelar pada fungsi label(). Pemanggil tanpa gelar tetap sah, sedangkan pemanggil yang mengisinya meletakkan nilainya pada posisi kedua. Nilai bawaan seperti String gelar = '' digunakan apabila argumennya tidak diberikan, sehingga label('Citra') menghasilkan Citra tanpa tambahan apa pun.

Parameter bernama ditulis dalam kurung kurawal dan dipanggil dengan menyebut namanya, sebagaimana acara: dan sapaan: pada fungsi sapa(). Parameter bernama tanpa required bersifat opsional dan lazim diberi nilai bawaan. Kata kunci required menjadikan parameter tersebut wajib, dan Dart menolak pemanggilan yang tidak mengisinya. API Flutter secara luas mengikuti pendekatan ini karena hampir seluruh konfigurasi widget ditulis sebagai parameter bernama, sehingga maksud setiap nilai terbaca langsung pada tempat pemanggilannya.

Sintaks panah#

Tanda => adalah bentuk ringkas untuk tubuh fungsi yang hanya mengembalikan hasil satu ekspresi. Fungsi double kaliDua(double angka) => angka * 2; identik dengan fungsi yang menulis return angka * 2; secara eksplisit. Bentuk ini juga sah untuk fungsi tanpa nilai kembali, misalnya void cetak(String pesan) => print(pesan);, yang tubuhnya setara dengan satu pernyataan. Ekspresi bersyarat seperti kondisi ? a : b tetap diperbolehkan karena bentuknya satu ekspresi utuh.

Sisi kanan panah memiliki batasan penting, yaitu tidak boleh berisi pernyataan bertingkat. Perulangan dan percabangan dengan blok {} tidak dapat ditempatkan pada bentuk ini, karena bentuk panah bukan pengganti kurung kurawal. Apabila logikanya berkembang menjadi lebih dari satu langkah, gunakan kembali tubuh blok dan tulis return secara eksplisit. Getter pada topik Kelas juga menggunakan bentuk ini, dan sintaks panah memang terus muncul dalam kode Flutter.

Fungsi sebagai nilai#

Fungsi di Dart adalah objek, sehingga dapat disimpan dalam variabel, diteruskan sebagai argumen, dan dikembalikan dari fungsi lain. Baris var operasi = kaliDua; pada program menyimpan referensi fungsi tanpa memanggilnya, lalu operasi(10) menjalankan fungsi yang sama melalui nama lain. Tipe fungsinya dapat dituliskan secara eksplisit, misalnya double Function(double) yang menyatakan bahwa fungsi menerima double dan mengembalikan double. Penulisan tipe ini membantu pemeriksa tipe dan membuat maksud kode terbaca.

Kemampuan ini menjadi tulang punggung pola callback pada kode penanganan peristiwa. Bentuk int Function() pada fungsi buatPenghitung() menyatakan nilai kembalian berupa fungsi tanpa parameter yang menghasilkan int. Kode Flutter menggunakan pola ini pada hampir setiap interaksi, misalnya parameter onPressed yang menerima fungsi untuk dijalankan ketika tombol ditekan. Fungsi sebagai nilai dengan demikian menjadi keterampilan dasar dalam menulis kode Flutter.

Closure menangkap lingkupnya#

Closure adalah fungsi yang mengingat variabel dari lingkup tempat ia dibuat, meskipun eksekusi bagian yang melingkupinya sudah selesai. Pada fungsi buatPenghitung(), variabel hitungan hidup di dalam tubuhnya, sedangkan fungsi yang dikembalikan merujuk variabel itu pada setiap pemanggilan. Hasilnya, penghitungA() menaikkan hitungan miliknya sendiri dan tidak berbagi apa pun dengan penghitungB(), karena setiap pemanggilan buatPenghitung() membuat variabel hitungan yang baru.

Perilaku ini bukan fitur khusus, melainkan konsekuensi aturan lingkup (scope) Dart. Setiap fungsi, termasuk fungsi anonim () { ... } yang ditulis tanpa nama, selalu membaca variabel dari lingkup yang melingkupinya. Manfaatnya terlihat ketika data kecil perlu disimpan dekat dengan perilakunya, misalnya menghitung berapa kali sebuah tombol ditekan tanpa membuat kelas terpisah. Di Flutter, pola ini muncul ketika fungsi yang diteruskan ke widget membaca variabel lokal dari metode yang mendefinisikannya.

Kesalahan umum#

  • Menggabungkan parameter posisi opsional dan parameter bernama dalam satu fungsi. Dart hanya mengizinkan satu jenis parameter opsional per fungsi, sehingga deklarasi yang memuat [int b] dan {int c} sekaligus ditolak saat kompilasi. Perbaikannya, pilih satu jenis parameter opsional, atau jadikan keduanya parameter wajib.
  • Menulis pernyataan di sisi kanan sintaks panah. Bentuk => return nama; ditolak Dart karena return adalah pernyataan, bukan ekspresi. Perbaikannya, tulis => nama; untuk bentuk ringkas, atau gunakan tubuh blok dengan return eksplisit apabila logikanya bertingkat.
  • Menyertakan tanda kurung ketika hendak meneruskan fungsi sebagai nilai. Baris var operasi = kaliDua(); memanggil fungsi lalu menyimpan hasilnya, sedangkan maksud yang benar adalah var operasi = kaliDua;. Kekeliruan yang sama sering muncul di Flutter ketika parameter seperti onPressed diberi hasil pemanggilan alih-alih fungsinya, sehingga aksi terpanggil saat widget dibangun. Perbaikannya, teruskan fungsinya apa adanya, atau bungkus pemanggilan yang butuh argumen ke dalam fungsi anonim seperti () => simpan(nilai).

Ringkasan#

  • Parameter posisi opsional ditulis dalam kurung siku, sedangkan parameter bernama ditulis dalam kurung kurawal dengan required dan nilai bawaan.
  • Sintaks panah => meringkas tubuh fungsi satu ekspresi dan mengembalikan hasilnya secara implisit.
  • Fungsi adalah objek yang dapat disimpan, diteruskan sebagai argumen, dan dikembalikan, dengan tipe seperti int Function().
  • Closure menangkap variabel lingkup luarnya, sehingga setiap closure yang dibuat menyimpan state yang berdiri sendiri.

Referensi#

  • Functions — Dart — Dokumentasi resmi parameter opsional, parameter bernama, dan fungsi sebagai nilai.
  • Language — Dart — Halaman induk dokumentasi bahasa Dart yang memuat ringkasan seluruh fitur inti.