+32

Học nhanh ngôn ngữ Dart (Flutter) nhờ ngôn ngữ Kotlin (Phần 4 / 5)

1. Lời mở đầu

Series này được viết nhằm mục đích giúp những bạn đã hiểu biết về ngôn ngữ Kotlin hoặc Java có thể học nhanh ngôn ngữ Dart để code Flutter. Nếu bạn chưa đọc những phần trước, bạn có thể đọc lại tại đây

2. Class

Class trong Dart khá giống Java. Quy ước đặt tên class cũng giống Kotlin. Dưới đây mình liệt kê các thành phần cơ bản của một class gồm property (Dart gọi là data), constructor, member funtions (hay còn gọi là method), biến static và hàm static.

  • Muốn tạo biến hoặc hàm static thì thêm từ khóa static ở trước thôi
  • Dòng code User(this.id, this.name, this.age); trong Dart nó tương đương với đoạn code này trong Kotlin
constructor(id: Int, name: String, age: Int) {
        this.id = id
        this.name = name
        this.age = age
}
  • Nếu trong Class mà bạn không khai báo bất cứ một constructor nào cả, sẽ có một constructor rỗng (không có param) mặc định được tạo trong class của bạn và một khi bạn đã tạo ra được 1 constructor có param rồi thì constructor rỗng này sẽ biến mất. Tức là với code trên mà gọi var user = User(); sẽ lỗi vì không tìm thấy constructor. Cái này giống y Kotlin với Java.

3. Data class

Cũng sử dụng đoạn code trên. Nếu thử print đối tượng user: print(user); ta sẽ nhận được output là Instance of 'User' chứ không phải là User(id=1, name=Minh, age=18) như ta vẫn thường gặp bên Kotlin. Lý do là vì:

Dart không có hỗ trợ data class như Kotlin.

Đắng (facepalm). Mà không hỗ trợ data class thì phải overide hàm toString một cách thủ công thôi.

Bây giờ thì output đã giống Kotlin rồi:

User(id=1, name=Minh, age=18)

4. Named constructors

Dart cho phép chúng ta đặt tên constructors để tạo ra nhiều hơn 1 constructor. Mình thử tạo ra 3 constructor:

Output:

User(id=1, name=Minh, age=18)
User(id=0, name=null, age=18)
User(id=30, name=null, age=18)

Vì mấy biến name kia không có giá trị khởi tạo nên nó null. Dễ hiểu ha 😄

5. Redirecting constructors

Thuật ngữ này bên Dart nó giống Secondary constructors bên Kotlin ấy. Nó giúp ta tạo 1 constructor từ một constructor khác trong cùng class.

6. Import

Import file

Dart xem mỗi file .dart như 1 lib riêng biệt. Điều đó có nghĩa là dù 2 file .dart nằm trong 1 package bạn cũng phải dùng import. Cái này khác Kotlin ha.

Bây giờ thử tạo ra 2 file .dart. Trong file user.dart mình tạo 1 class là User. Trong file .dart còn lại mình sẽ tạo hàm main() và khởi tạo đối tượng User. Kết quả Dart nó yêu cầu phải import 'user.dart';

Thử move file user.dart vào 1 folder có tên là example rồi xem kết quả. Bây giờ đường dẫn thư mục đã đổi thành import 'example/user.dart';

Import thư viện core của Dart

Import thư viện core của Dart. Cú pháp import 'dart:xxx'. Ví dụ thư viện math: import 'dart:math'

Import lib từ dependency hoặc lib do mình code trong thư mục lib

Cú pháp: import 'package:file path'. Ví dụ:

import 'package:english_words/english_words.dart';

import as

Giả sử như chúng ta có 2 lib là lib1 và lib2. Cả 2 lib đều có class Element thì chúng ta sẽ phân biệt bằng cách dùng import ... as ...:

Import một phần của lib

Nếu chúng ta chỉ muốn lấy một phần trong lib, không muốn lấy tất cả. Có thể dùng show/hide

7. Access modifier

Trong Dart không có access modifier (public, private, protected, internal) như Kotlin hay Java. Default là public hết. Nếu bạn muốn tạo một biến, hàm hoặc constructor mà private thì thêm ký tự underscore _ trước cái tên.

Thử tạo thêm 1 file user.dart rồi tạo 1 class trong file này

class User {
  var id;
  var name;
  
  // biến private
  var _age;

  // hàm private
  void _printName() {
    print(name);
  }

  bool duTuoiXemPhim() => _age >= 18;
}

Giờ qua file main.dart để code hàm main:

Các bạn thấy nó báo lỗi không. Vậy là mình không thể gọi đến biến _age và hàm _printName() vì nó đã private

8. Setter, getter

Đã có biến private thì cần phải có hàm setter, getter đúng không nào 😄. Dart cung cấp cho chúng ta 2 keyword là setget để làm điều này.

9. Factory constructor, Singleton

Dart cung cấp cho chúng ta factory keyword để tạo ra 1 constructor đặc biệt, khi sử dụng constructor này để tạo đối tượng nó sẽ không tạo ra một đối tượng mới nếu nó thấy đã có một đối tượng có sẵn rồi. Nói ngắn gọn là nó hoạt động giống như Singleton Pattern vậy. Vậy mình sẽ thử dùng factory để tạo ra một class Singleton.

class Singleton {
  static final Singleton _singleton = Singleton._internal();

  // factory constructor
  factory Singleton() {
    return _singleton;
  }

  // Như mình đã nói ở trên thì đây là private constructor 
  Singleton._internal();
}

Bây giờ thử test xem 2 đối tượng có cùng địa chỉ hay không?

void main() {
  var s1 = Singleton();
  var s2 = Singleton();

  // hàm identical giúp ta so sánh cùng địa chỉ hay ko?. Giống toán tử === bên Kotlin
  print(identical(s1, s2));  // in ra: true
}

10. Cascade notation

Cái này khá giống apply trong Kotlin. Cú pháp: ..

Các bạn thấy Dart lỗi ở lệnh print không?. Đó là lý do vì sao mình nói là khá giống mà không nói giống hoàn toàn :v. .. trong Dart nó chỉ gọi được instance method/property thôi còn hàm print thì nó chịu. Vậy nên chỉ cần chỉnh lại như thế này là được:

11. Enum

Đặt tên Enum trong Dart cũng viết kiểu UpperCamelCase như Kotlin (VD: TypeOfStudent). Nhưng các enum values trong Dart viết style lowerCamelCase (VD: goodStudent), còn Kotlin viết chữ hoa kèm dấu _ (VD: GOOD_STUDENT).

12. Abstract class

Abstract class nó cũng giống class bình thường thôi, cũng có constructor, method và các property. Chỉ khác ở chỗ là nó có thêm abstract method (phương thức không có body). VD:

13. Kế thừa

Dart không hỗ trợ đa kế thừa, tức là một class chỉ có thể có tối đa một class cha. Chúng ta sử dụng từ khóa extend để kế thừa class cha. Class cha có thể là abstract class cũng có thể là class thường. Khi extend abstract class, chúng ta bắt buộc phải overide lại các abstract method (phương thức không có body).

Dart không có phân biệt Interface với Class như Kotlin mà nó xem mỗi class chính là interface luôn. Như vậy một class có thể kế thừa một class khác bằng từ khóa implement. Một class chỉ có thể extend một class nhưng có thể implement nhiều class khác. Trong Dart không có khái niệm final class như Kotlin, có nghĩa là tất cả class đều có thể được kế thừa.

Vì Interface trong Kotlin là phương thức không thân nên khi override lại sẽ không bị duplicate code. Còn Dart thì code trong class Dog bị duplicate code với class mà nó implement là Walkable, Flyable như trong ảnh mình có note. Nếu như bạn chỉ muốn tái sử dụng code của lớp Walkable, Flyable mà không muốn override lại một hàm giống y chang như vậy (duplicate) thì bạn sử dụng with thay cho implements.

with

Bây giờ mình sẽ ví dụ đoạn code có sử dụng cả extend, implementswith:

Sử dụng with bạn sẽ không bị ép buộc phải override hết function, property, có nghĩa là bạn thích thì bạn cũng có thể override lại còn không thì sẽ reuse lại code. Còn sử dụng implements thì buộc phải override lại hết, có nghĩa là sẽ có trường hợp bị duplicate code như ví dụ trên của mình 😄

Cái with này trong Dart người ta gọi là Mixins. Thuật ngữ này có thể khá lạ lẫm với bạn nên mình xin trích nguyên văn từ doc là:

Mixins are a way of reusing a class’s code in multiple class hierarchies.

Hiểu nôm na là một nơi cung cấp method và property cho lớp khác sử dụng mà không cần phải là cha cũng chúng.

mixin

Dart cung cấp thêm cho chúng ta từ khóa mixin. Trong ví dụ Kế thừa ở trên, bạn thử replace class Walkable bằng mixin Walkable thì kết quả vẫn không đổi. mixin khác class thông thường ở chỗ:

  • mixin không có constructor nên không thể tạo đối tượng.
  • mixin chỉ có thể sử dụng để implements hoặc with chứ không thể extends
  • mixin có thể giới hạn những class nào được phép sử dụng code của mình bằng từ khóa mixin on

mixin on

Như mình đã nói ở trên, ta sử dụng mixin on để giới hạn những class nào được phép sử dụng code của mình.

Nhìn vào ảnh trên có thể dễ hiểu rằng:

  • mixin XOnA chỉ được dành cho những class nào là con của class A cụ thể là class P, tương tự như thế với mixin YOnB.
  • class T không được phép sử dụng mixin YOnB vì nó không phải là con của class B.
  • class R không được phép sử dụng mixin XOnA vì nó không phải là con của class A. Chính xác thì class R là con của class Object nên sửa lại thế này sẽ ok:

14. Override toán tử

Trong Kotlin, mỗi class cho phép ta override hàm equal thì Dart overide toán tử ==

Ngoài toán tử ==, bạn cũng có thể override các toán tử khác trong bảng sau nếu muốn

Kết luận

Đến giờ phút này đây thì các bạn đã có thể code Dart được rồi đấy 😄. Hy vọng các bạn tiếp tục theo dõi những phần tiếp theo 😄

Tham khảo: https://dart.dev/guides

https://medium.com/flutter-community/https-medium-com-shubhamhackzz-dart-for-flutter-mixins-in-dart-f8bb10a3d341

https://medium.com/@manoelsrs/dart-extends-vs-implements-vs-with-b070f9637b36

Đọc tiếp phần 5: Học nhanh ngôn ngữ Dart (Flutter) nhờ ngôn ngữ Kotlin (Phần 5)


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí