Pengantar Dart untuk Developer Java

Dart adalah bahasa pemrograman untuk Flutter, toolkit UI Google untuk mem-build aplikasi seluler, web, dan desktop yang cantik dan dikompilasi secara native dari satu codebase.

Codelab ini akan memperkenalkan Dart yang berfokus pada fitur yang mungkin tidak diduga oleh developer Java. Anda dapat menulis fungsi Dart dalam 1 menit, skrip dalam 5 menit, dan aplikasi dalam 10 menit.

Yang akan Anda pelajari

  • Cara membuat konstruktor
  • Berbagai cara untuk menentukan parameter
  • Waktu dan cara membuat pengambil dan penyetel
  • Cara Dart menangani privasi
  • Cara membuat factory
  • Cara kerja pemrograman fungsional di Dart
  • Konsep Dart inti lainnya

Yang akan Anda butuhkan

Untuk menyelesaikan codelab ini, yang Anda butuhkan hanyalah browser.

Anda akan menulis dan menjalankan semua contoh di DartPad, alat interaktif berbasis browser yang memungkinkan Anda bermain dengan fitur bahasa dan library inti Dart. Atau, Anda dapat menggunakan IDE, seperti WebStorm, IntelliJ dengan plugin Dart, atau Kode Visual Studio dengan ekstensi Kode Dart.

Apa yang ingin Anda pelajari dari codelab ini?

Saya baru mengenal topik ini, jadi saya ingin melihat ringkasan yang bagus. Saya sedikit paham soal topik ini, tetapi saya butuh penyegaran. Saya sedang mencari kode contoh untuk digunakan dalam project saya. Saya sedang mencari penjelasan tentang sesuatu yang spesifik.

Anda akan mulai dengan mem-build class Dart sederhana dengan fungsi yang sama seperti class Bicycle dari Tutorial Java. Class Bicycle berisi beberapa variabel instance pribadi dengan pengambil dan penyetel. Metode main() membuat instance Bicycle dan mencetaknya ke konsol.

99c813a1913dcc42.png c97a12197358d545.png

Meluncurkan DartPad

Codelab ini menyediakan instance DartPad baru untuk setiap kumpulan latihan. Link di bawah ini akan membuka instance baru, yang berisi contoh "Halo" default. Anda dapat terus menggunakan DartPad yang sama di seluruh codelab, tetapi jika Anda mengklik Reset, hasil kerja Anda akan hilang karena DartPad akan mengembalikan Anda ke contoh default.

b2f84ff91b0e1396.png Buka DartPad

Menentukan class Bicycle

b2f84ff91b0e1396.png Di atas fungsi main(), tambahkan class Bicycle dengan tiga variabel instance. Hapus juga konten dari main(), seperti yang ditampilkan dalam cuplikan kode berikut:

class Bicycle {
  int cadence;
  int speed;
  int gear;
}

void main() {
}

cf1e10b838bf60ee.png Pengamatan

  • Metode utama Dart diberi nama main(). Jika memerlukan akses ke argumen command line, Anda dapat menambahkan: main(List<String> args).
  • Metode main() berada di level teratas. Di Dart, Anda dapat menentukan kode di luar class. Variabel, fungsi, pengambil, dan penyetel dapat berada di luar class.
  • Contoh Java asli mendeklarasikan variabel instance pribadi menggunakan tag private, yang tidak Dart gunakan. Anda akan mempelajari privasi lebih lanjut nanti di bagian "Menambahkan variabel hanya baca".
  • Baik main() maupun Bicycle tidak dideklarasikan sebagai public, karena semua ID bersifat publik secara default. Dart tidak memiliki kata kunci untuk public, private, atau protected.
  • Dengan contoh ini, penganalisis Dart akan memunculkan error yang menginformasikan bahwa variabel harus diinisialisasi karena bersifat non-nullable. Anda akan memperbaiki hal tersebut di bagian berikutnya.
  • Berdasarkan konvensi, Dart menggunakan indentasi dua karakter, bukan empat. Berkat alat yang berguna yang disebut dartfmt, Anda tidak perlu khawatir tentang konvensi spasi kosong Dart. Karena konvensi kode Dart (Dart Efektif) menyatakan bahwa, "Aturan penanganan spasi kosong resmi untuk Dart adalah apa pun yang dihasilkan dartfmt."

Menentukan konstruktor Bicycle

b2f84ff91b0e1396.png Tambahkan konstruktor berikut ke class Bicycle:

Bicycle(this.cadence, this.speed, this.gear);

cf1e10b838bf60ee.png Pengamatan

  • Konstruktor ini tidak memiliki isi yang valid di Dart.
  • Jika Anda lupa titik koma (;) di akhir konstruktor tanpa isi, DartPad akan menampilkan error berikut: "Isi fungsi harus dimasukkan".
  • Menggunakan this dalam daftar parameter konstruktor adalah pintasan yang berguna untuk menetapkan nilai ke variabel instance.
  • Kode di atas setara dengan kode berikut:
Bicycle(int cadence, int speed, int gear) {
  this.cadence = cadence;
  this.speed = speed;
  this.gear = gear;
}

Memformat kode

Memformat ulang kode Dart kapan saja dengan mengklik Format di bagian atas UI DartPad. Pemformatan ulang sangat berguna saat Anda menempelkan kode ke DartPad dan justifikasinya tidak aktif.

b2f84ff91b0e1396.png Klik Format.

Membuat instance dan mencetak instance Bicycle

b2f84ff91b0e1396.png Tambahkan kode berikut ke fungsi main():

void main() {
  var bike = new Bicycle(2, 0, 1);
  print(bike);
}

b2f84ff91b0e1396.png Hapus kata kunci new opsional:

var bike = Bicycle(2, 0, 1);

cf1e10b838bf60ee.pngPengamatan

  • Kata kunci new menjadi opsional di Dart 2.
  • Jika Anda mengetahui bahwa nilai variabel tidak akan berubah, artinya Anda dapat menggunakan final, bukan var.

Menjalankan contoh

b2f84ff91b0e1396.png Eksekusi contoh dengan mengklik Run di bagian atas jendela DartPad. Jika Run tidak diaktifkan, lihat bagian Ada Masalah nanti di halaman ini.

Anda akan melihat output berikut:

Instance of 'Bicycle'

cf1e10b838bf60ee.png Pengamatan

  • Tidak ada error atau peringatan yang akan muncul, yang menunjukkan bahwa inferensi jenis berfungsi, dan penganalisis akan menyimpulkan bahwa pernyataan yang dimulai dengan var bike = menentukan instance Bicycle.

Meningkatkan output

Meskipun output "Instance Bicycle'" benar, output tersebut tidak cukup informatif. Semua class Dart memiliki metode toString() yang dapat Anda ganti untuk memberikan output yang lebih berguna.

b2f84ff91b0e1396.png Tambahkan metode toString() berikut di mana saja dalam class Bicycle:

@override
String toString() => 'Bicycle: $speed mph';

cf1e10b838bf60ee.png Pengamatan

  • Anotasi @override memberi tahu penganalisis bahwa Anda sengaja mengganti anggota. Penganalisis akan memunculkan error jika Anda tidak dapat melakukan penggantian dengan benar.
  • Dart mendukung tanda kutip tunggal atau ganda saat menentukan string.
  • Gunakan jenis interpolasi string untuk menempatkan nilai ekspresi di dalam literal string: ${expression}. Jika ekspresi adalah ID, Anda dapat melewati tanda kurung kurawal: $variableName.
  • Persingkat fungsi atau metode satu baris menggunakan notasi fungsi panah (=>).

Menjalankan contoh

b2f84ff91b0e1396.png Klik Run.

Anda akan melihat output berikut:

Bicycle: 0 mph

Ada Masalah? Periksa kode Anda.

Menambahkan variabel hanya baca

Contoh Java asli mendefinisikan speed sebagai variabel hanya-baca—Contoh Java asli juga mendeklarasikannya sebagai pribadi dan hanya menyediakan pengambil. Selanjutnya, Anda akan memberikan fungsi yang sama di Dart.

b2f84ff91b0e1396.png Buka bicycle.dart di DartPad (atau lanjutkan menggunakan salinan Anda).

Untuk menandai ID Dart sebagai pribadi ke library-nya, mulai namanya dengan garis bawah (_). Anda dapat mengonversi speed menjadi hanya baca dengan mengubah namanya dan menambahkan pengambil.

Menjadikan kecepatan sebagai variabel instance hanya baca yang bersifat pribadi

b2f84ff91b0e1396.png Pada konstruktor Bicycle, hapus parameter speed:

Bicycle(this.cadence, this.gear);

b2f84ff91b0e1396.png Dalam kode main(), hapus parameter kedua (speed) dari panggilan ke konstruktor Bicycle:

var bike = Bicycle(2, 1);

b2f84ff91b0e1396.png Ubah kemunculan speed yang tersisa ke _speed. (Dua tempat)

b2f84ff91b0e1396.pnglakukan inisialisasi _speed ke 0:

int _speed = 0;

b2f84ff91b0e1396.png Tambahkan pengambil berikut ke class Bicycle:

int get speed => _speed;

cf1e10b838bf60ee.png Pengamatan

  • Setiap variabel (meskipun jika berupa angka) harus diinisialisasi atau dideklarasikan sebagai nullable dengan menambahkan ? ke deklarasi jenisnya.
  • Compiler Dart menerapkan privasi library untuk setiap ID yang diawali dengan garis bawah. Privasi library umumnya berarti bahwa ID hanya terlihat di dalam file (bukan hanya class) tempat ID ditentukan.
  • Secara default, Dart menyediakan pengambil dan penyetel implisit untuk semua variabel instance publik. Anda tidak perlu menentukan pengambil atau penyetel Anda sendiri kecuali ingin menerapkan variabel hanya baca atau hanya tulis, menghitung atau memverifikasi nilai, atau memperbarui nilai di tempat lain.
  • Contoh Java asli memberikan pengambil dan penyetel untuk cadence dan gear. Contoh Dart tidak memerlukan pengambil dan penyetel eksplisit untuk kode tersebut sehingga hanya menggunakan variabel instance.
  • Anda dapat memulai dengan kolom sederhana, seperti bike.cadence, dan kemudian memfaktorkan ulang kolom tersebut agar dapat menggunakan pengambil dan penyetel. API tetap sama. Dengan kata lain, beralih dari sebuah kolom ke pengambil dan penyetel bukanlah perubahan yang dapat menyebabkan gangguan di Dart.

Menyelesaikan penerapan "speed" sebagai variabel instance hanya baca

b2f84ff91b0e1396.png Tambahkan metode berikut ke class Bicycle:

void applyBrake(int decrement) {
  _speed -= decrement;
}

void speedUp(int increment) {
  _speed += increment;
}

Contoh Dart akhir terlihat mirip dengan Java asli, tetapi lebih ringkas, hanya 23 baris, bukan 40:

class Bicycle {
  int cadence;
  int _speed = 0;
  int get speed => _speed;
  int gear;

  Bicycle(this.cadence, this.gear);

  void applyBrake(int decrement) {
    _speed -= decrement;
  }

  void speedUp(int increment) {
    _speed += increment;
  }

  @override
  String toString() => 'Bicycle: $_speed mph';
}

void main() {
  var bike = Bicycle(2, 1);
  print(bike);
}

Ada Masalah? Periksa kode Anda.

Latihan berikutnya menentukan class Rectangle, contoh lain dari Tutorial Java.

Kode Java menampilkan pembebanan konstruktor, yang merupakan praktik umum di Java tempat konstruktor memiliki nama yang sama, tetapi jumlah atau jenis parameternya berbeda. Dart tidak mendukung pembebanan konstruktor dan menangani situasi ini secara berbeda, seperti yang akan Anda lihat di bagian ini.

b2f84ff91b0e1396.png Buka contoh Rectangle di DartPad.

Menambahkan konstruktor Rectangle

b2f84ff91b0e1396.png Tambahkan satu konstruktor kosong yang menggantikan keempat konstruktor dalam contoh Java:

Rectangle({this.origin = const Point(0, 0), this.width = 0, this.height = 0});

Konstruktor ini menggunakan opsi parameter bernama.

cf1e10b838bf60ee.png Pengamatan

  • this.origin, this.width, dan this.height menggunakan trik singkat untuk menetapkan variabel instance dalam deklarasi konstruktor.
  • this.origin, this.width, dan this.height adalah opsi parameter bernama. Parameter bernama diberi tanda kurung kurawal ({}).
  • Sintaks this.origin = const Point(0, 0) menentukan nilai default Point(0,0) untuk variabel instance origin. Default yang ditentukan harus berupa konstan waktu kompilasi. Konstruktor ini menyediakan nilai default untuk ketiga variabel instance.

Meningkatkan output

b2f84ff91b0e1396.png Tambahkan fungsi toString() berikut ke class Rectangle:

@override
String toString() =>
      'Origin: (${origin.x}, ${origin.y}), width: $width, height: $height';

Menggunakan konstruktor

b2f84ff91b0e1396.png Ganti main() dengan kode berikut untuk memverifikasi bahwa Anda dapat membuat instance Rectangle dengan hanya menggunakan parameter yang diperlukan:

main() {
  print(Rectangle(origin: const Point(10, 20), width: 100, height: 200));
  print(Rectangle(origin: const Point(10, 10)));
  print(Rectangle(width: 200));
  print(Rectangle());
}

cf1e10b838bf60ee.png Pengamatan

  • untuk konstruktor yang setara, konstruktor Dart untuk Rectangle berisi satu baris kode saja, sedangkan versi Java berisi 16 baris kode.

Menjalankan contoh

Anda akan melihat output berikut:

Origin: (10, 20), width: 100, height: 200
Origin: (10, 10), width: 0, height: 0
Origin: (0, 0), width: 200, height: 0
Origin: (0, 0), width: 0, height: 0

Ada Masalah? Periksa kode Anda.

Factory, pola desain yang biasa digunakan di Java memiliki beberapa keuntungan dibandingkan dengan pembuatan instance objek langsung, seperti menyembunyikan detail pembuatan instance, yang dapat menampilkan subjenis dari jenis nilai yang ditampilkan factory, dan menampilkan objek yang sudah ada secara opsional, bukan objek baru.

Langkah ini mendemonstrasikan dua cara untuk menerapkan factory pembuatan bentuk:

  • Opsi 1: Membuat fungsi level teratas.
  • Opsi 2: Membuat konstruktor factory.

Untuk latihan ini, Anda akan menggunakan contoh Shape, yang membuat instance bentuk dan mencetak area yang dikomputasi:

import 'dart:math';

abstract class Shape {
  num get area;
}

class Circle implements Shape {
  final num radius;
  Circle(this.radius);
  num get area => pi * pow(radius, 2);
}

class Square implements Shape {
  final num side;
  Square(this.side);
  num get area => pow(side, 2);
}

main() {
  final circle = Circle(2);
  final square = Square(2);
  print(circle.area);
  print(square.area);
}

b2f84ff91b0e1396.png Buka contoh Shape di DartPad.

Di area konsol, Anda akan melihat area lingkaran dan persegi yang dikomputasi:

12.566370614359172
4

cf1e10b838bf60ee.png Pengamatan

  • Dart mendukung class abstrak.
  • Anda dapat menentukan beberapa class dalam satu file.
  • dart:math adalah salah satu library inti Dart. Library inti lainnya mencakup dart:core, dart:async, dart:convert, dan dart:collection.
  • Berdasarkan konvensi, konstan library Dart adalah lowerCamelCase (misalnya, pi bukan PI). Jika Anda penasaran dengan alasannya, lihat panduan gaya LEBIH BAIK gunakan lowerCaseCover untuk nama konstan.
  • Kode berikut menunjukkan dua pengambil yang menghitung nilai: num get area => pi * pow(radius, 2); // Circle num get area => pow(side, 2); // Square

Opsi 1: Membuat fungsi level teratas

b2f84ff91b0e1396.png Implementasikan factory sebagai fungsi level teratas dengan menambahkan fungsi berikut pada level tertinggi (di luar class mana pun):

Shape shapeFactory(String type) {
  if (type == 'circle') return Circle(2);
  if (type == 'square') return Square(2);
  throw 'Can\'t create $type.';
}

b2f84ff91b0e1396.png Panggil fungsi factory dengan mengganti dua baris pertama dalam metode main():

  final circle = shapeFactory('circle');
  final square = shapeFactory('square');

Menjalankan contoh

Output harus sama seperti sebelumnya.

cf1e10b838bf60ee.png Pengamatan

  • Jika fungsi dipanggil dengan string apa pun selain 'circle' atau 'square', fungsi tersebut akan menampilkan pengecualian.
  • SDK Dart menentukan class untuk banyak pengecualian umum, atau Anda dapat mengimplementasikan class Exception untuk membuat pengecualian yang lebih spesifik atau (seperti dalam contoh ini) Anda dapat menampilkan string yang mendeskripsikan masalah yang ditemukan.
  • Saat pengecualian ditemukan, DartPad melaporkan Uncaught. Untuk melihat informasi yang lebih bermanfaat, gabungkan kode dalam pernyataan try-catch dan cetak pengecualian. Sebagai latihan opsional, lihat contoh DartPad ini.
  • Untuk menggunakan tanda kutip tunggal dalam string, pisahkan tanda kutip tersemat menggunakan garis miring ('Can\'t create $type.') atau tentukan string dengan tanda kutip ganda ("Can't create $type.").

Ada Masalah? Periksa kode Anda.

Opsi 2: Membuat konstruktor factory

Gunakan kata kunci factory Dart untuk membuat konstruktor factory.

b2f84ff91b0e1396.png Tambahkan konstruktor factory ke class Shape abstrak:

abstract class Shape {
  factory Shape(String type) {
    if (type == 'circle') return Circle(2);
    if (type == 'square') return Square(2);
    throw 'Can\'t create $type.';
  }
  num get area;
}

b2f84ff91b0e1396.png Ganti dua baris pertama main() dengan kode berikut untuk membuat instance bentuk:

  final circle = Shape('circle');
  final square = Shape('square');

b2f84ff91b0e1396.png Hapus fungsi shapeFactory() yang sebelumnya ditambahkan.

cf1e10b838bf60ee.png Pengamatan

  • Kode di konstruktor factory identik dengan kode yang digunakan dalam fungsi shapeFactory().

Ada Masalah? Periksa kode Anda.

Bahasa Dart tidak menyertakan kata kunci interface karena setiap class menentukan antarmuka.

b2f84ff91b0e1396.png Buka contoh Shape di DartPad (atau terus gunakan salinan Anda).

b2f84ff91b0e1396.png Tambahkan class CircleMock yang menerapkan antarmuka Circle:

class CircleMock implements Circle {}

b2f84ff91b0e1396.png Anda akan melihat error "Implementasi konkret hilang" karena CircleMock tidak mewarisi implementasi Circle—hanya menggunakan antarmuka. Perbaiki error ini dengan menentukan variabel instance area dan radius:

class CircleMock implements Circle {
  num area = 0;
  num radius = 0;
}

cf1e10b838bf60ee.png Pengamatan

  • Meskipun class CircleMock tidak menentukan perilaku apa pun, class tersebut merupakan Dart yang valid—penganalisis tidak akan memunculkan error.
  • Variabel instance area dari CircleMock mengimplementasikan pengambil area dari Circle.

Ada Masalah? Periksa kode Anda.

Dalam pemrograman fungsional, Anda dapat melakukan hal-hal berikut:

  • Meneruskan fungsi sebagai argumen.
  • Menetapkan fungsi ke variabel.
  • Mendekonstruksi fungsi yang membawa beberapa argumen ke dalam urutan fungsi yang masing-masing mengambil satu argumen (juga disebut currying).
  • Membuat fungsi tanpa nama yang dapat digunakan sebagai nilai konstan (disebut juga ekspresi lambda; ekspresi lambda ditambahkan ke Java dalam rilis JDK 8).

Dart mendukung semua fitur tersebut. Di Dart, fungsi dianggap sebagai objek dan memiliki jenis, Function. Artinya, fungsi dapat ditetapkan ke variabel atau diteruskan sebagai argumen ke fungsi lainnya. Anda juga dapat memanggil instance class Dart seolah-olah itu merupakan sebuah fungsi, seperti contoh ini.

Contoh berikut menggunakan kode imperatif (bukan gaya fungsional):

String scream(int length) => "A${'a' * length}h!";

main() {
  final values = [1, 2, 3, 5, 10, 50];
  for (var length in values) {
    print(scream(length));
  }
}

b2f84ff91b0e1396.png Buka contoh Scream di DartPad.

Output akan terlihat seperti berikut:

Aah!
Aaah!
Aaaah!
Aaaaaah!
Aaaaaaaaaaah!
Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah!

cf1e10b838bf60ee.png Pengamatan

  • Saat menggunakan jenis interpolasi string, string ${'a' * length} mengevaluasi "karakter 'a' yang diulang length kali".

Mengonversi kode imperatif ke fungsional

b2f84ff91b0e1396.png Hapus loop for() {...} imperatif di main() dan ganti dengan satu baris kode yang menggunakan metode rantai:

  values.map(scream).forEach(print);

Menjalankan contoh

Pendekatan fungsional mencetak enam teriakan yang sama dengan contoh imperatif.

Ada Masalah? Periksa kode Anda.

Menggunakan lebih banyak fitur Iterable

Class List dan Iterable inti mendukung fold(), where(), join(), skip(), dan lainnya. Dart juga dilengkapi dukungan untuk peta dan set.

b2f84ff91b0e1396.png Ganti baris values.map() di main() dengan cara berikut:

  values.skip(1).take(3).map(scream).forEach(print);

Menjalankan contoh

Output akan terlihat seperti berikut:

Aaah!
Aaaah!
Aaaaaah!

cf1e10b838bf60ee.png Pengamatan

  • skip(1) melewati nilai pertama, 1, di literal daftar values.
  • take(3) mendapatkan 3 nilai berikutnya—2, 3, dan 5—di literal daftar values.
  • Nilai lainnya akan dilewati.

Ada Masalah? Periksa kode Anda.

Dengan menyelesaikan codelab ini, Anda mendapatkan pengetahuan tentang beberapa perbedaan antara Java dan Dart. Dart cukup mudah untuk dipelajari. Selain itu, Dart juga dilengkapi dengan library inti dan tersedia beragam kumpulan paket yang akan meningkatkan produktivitas Anda. Dart diskalakan dengan baik untuk aplikasi besar. Ratusan engineer Google menggunakan Dart untuk menulis aplikasi Mission Critical yang mendatangkan sebagian besar pendapatan Google.

Langkah berikutnya

Codelab selama 20 menit terlalu singkat untuk menunjukkan semua perbedaan antara Java dan Dart. Misalnya, codelab ini belum mencakup:

Jika Anda ingin melihat cara kerja teknologi Dart, coba codelab Flutter.

Pelajari lebih lanjut

Anda dapat mempelajari Dart lebih lanjut melalui artikel, referensi, dan situs berikut.

Artikel

Referensi

Situs