Hướng dẫn và ví dụ phương thức trong Dart
1. Phương thức là gì?
Trong ngôn ngữ lập trình Dart, phương thức là một khối mã (block of code) được định nghĩa bên trong một lớp và chỉ chạy khi được gọi. Các phương thức chia nhiệm vụ lớn thành các phần nhỏ và thực hiện một hoạt động cụ thể của chương trình. Quá trình này làm tăng khả năng tái sử dụng mã và nâng cao cách tiếp cận mô-đun của chương trình.
Về cơ bản, phương thức được chia làm 3 loại:
- Phương thức thông thường (không tĩnh và không trừu tượng).
- Phương thức tĩnh (static method).
- Phương thức trừu tượng (abstract method).
2. Phương thức thông thường
Cú pháp định nghĩa một phương thức thông thường:
return_type method_name(arguments) {
// statement(s)
}
- return_type: Kiểu dữ liệu trả về của phương thức. Sử dụng từ khoá void như một kiểu trả về nếu phương thức không trả về gì cả.
- method_name: Tên phương thức. Khác với các ngôn ngữ khác như Java, Dart không cho phép 2 phương thức cùng tên, kể cả khi chúng có các tham số khác nhau. Điều này là cần thiết để có thể chuyển đổi mã Dart sang JavaScript.
- arguments: Các tham số của phương thức.
Ví dụ: Lớp Cat và phương thức sayHello(..) của nó. Để gọi phương thức sayHello(..) bạn phải tạo một đối tượng Cat và gọi phương thức bằng cách sử dụng ký hiệu dấu chấm (dot notation).
method_ex1.dart
class Cat {
void sayHello(String name) {
print("Hello $name");
}
}
void main() {
Cat tom = new Cat(); // Create a Cat object.
tom.sayHello("Jerry"); // Call the method through the object.
}
Output:
Hello Jerry
Một phương thức có thể chứa 0, 1 hoặc nhiều tham số, các tham số cách nhau bởi dấu phẩy.
method_ex2.dart
class Rectangle {
int width;
int height;
Rectangle(this.width, this.height); // Constructor
int getArea() {
return width * height;
}
void changeWidthHeight(int newWidth, int newHeight) {
width = newWidth;
height = newHeight;
}
void showMe() {
print('I am a rectangle, width: $width, height: $height');
}
}
void main() {
Rectangle rec = Rectangle(5, 10); // Create an object.
rec.showMe(); // Call the method.
var area = rec.getArea(); // Call the method.
print('Area: $area');
print(' --- Change width and height --- ');
rec.changeWidthHeight(25, 15); // Set newWidth, newHeight
rec.showMe(); // Call the method.
area = rec.getArea(); // Call the method.
print('Area: $area');
}
Output:
I am a rectangle, width: 5, height: 10
Area: 50
--- Change width and height ---
I am a rectangle, width: 25, height: 15
Area: 375
3. Phương thức tĩnh
Dart sử dụng từ khoá static và cú pháp định nghĩa một phương thức thông thường để định nghĩa một phương thức tĩnh.
Cú pháp định nghĩa phương thức tĩnh:
static return_type method_name(arguments) {
// statement(s)
}
Các đặc điểm của phương thức tĩnh:
- Phương thức tĩnh được gọi thông qua tên lớp và ký hiệu dấu chấm. Chẳng hạn MyUtility.sum(100, 50). Tên lớp và ký hiệu dấu chấm có thể được bỏ qua nếu gọi phương thức tĩnh trong nội bộ lớp đó.
- Các thành viên không tĩnh của một lớp không thể xuất hiện trong phương thức tĩnh trừ khi chúng được truy cập thông qua đối tượng (Xem thêm ví dụ phía dưới).
method_static_ex1.dart
class MyUtility {
static int sum(int a, int b) {
return a + b;
}
static int minus(int a, int b) {
return a - b;
}
}
void main() {
var result = MyUtility.sum(100, 50);
print('Sum Result: $result');
result = MyUtility.minus(100, 50);
print('Minus Result: $result');
}
Output:
Sum Result: 150
Minus Result: 50
Tiếp theo, nhìn vào ví dụ dưới đây:
- side1, side2 và side3 là các trường (field) không tĩnh của lớp Triangle, chúng không thể xuất hiện trong phương thức tĩnh.
- Gọi phương tĩnh trong nội bộ lớp đã định nghĩa nó có thể không cần tên lớp và ký hiệu dấu chấm.
method_static_ex2.dart
class Triangle {
late double side1;
late double side2;
late double side3;
static const String DEFAULT_COLOR = 'blue'; // Static field
Triangle(double s1, double s2, double s3) {
bool valid = isValidSides(s1, s2, s3); // Check if all sides are valid.
if(!valid) {
throw ArgumentError('Invalid Sides!'); // Throw an Error.
}
side1 = s1;
side2 = s2;
side3 = s3;
}
static bool isValidSides(double s1, double s2, double s3) {
if(s1 < 0 || s2 < 0 || s3 < 0) {
return false;
}
return s1 + s2 > s3 && s1 + s3 > s2 && s2 + s3 > s1;
}
bool isEquilateralTriangle() {
return side1 == side2 && side2 == side3;
}
}
void main() {
bool valid = Triangle.isValidSides(6, 8, 10);
print('Are sides 6, 8 and 10 valid to make a triangle? $valid');
var triangle = Triangle(3.0, 4.0, 5.0);
// Check if the triangle is equilateral triangle.
var check = triangle.isEquilateralTriangle();
print('Is Equilateral Triangle? $check');
}
Output:
Are sides 6, 8 and 10 valid to make a triangle? true
Is Equilateral Triangle? false
- Exceptions
- Property trong Dart
4. Phương thức trừu tượng
Trong ngôn ngữ Dart, phương thức trừu tượng là một phương thức không tĩnh và không có thân (body).
abstract return_type method_name(arguments);
Một lớp có ít nhất một phương thức trừu tượng phải được khai báo là trừu tượng. Một trong các lớp con của nó sẽ ghi đè (override) các phương thức trừu tượng và viết nội dung cho chúng.
Ví dụ: Lớp Person dưới đây phải được khai báo là lớp trừu tượng vì nó có ít nhất một phương thức trừu tượng.
method_abstract_ex1.dart
abstract class Person {
void sayHello(); // An abstract method.
}
class EnglishPerson extends Person {
void sayHello() {
print("Hello");
}
}
class RussianPerson extends Person {
void sayHello() {
print("Привет");
}
}
void main() {
Person enPerson = EnglishPerson();
enPerson.sayHello();
Person ruPerson = RussianPerson();
ruPerson.sayHello();
}
Output:
Hello
Привет
- Dart Classes
5. Các tham số tuỳ chọn
Như đã đề cập ở trên, các lớp trong Dart và JavaScript không hỗ trợ các phương thức trùng tên, nhưng một phương thức có thể bao gồm các tham số tuỳ chọn.
Cú pháp:
// Non-static method with optional arguments:
return_type method_name(typeM argM, typeN argN,[typeP? argP, typeQ? argQ]){
// statement(s)
}
// Static method with optional arguments:
static return_type method_name(typeM argM, typeN argN,[typeP? argP, typeQ? argQ]){
// statement(s)
}
// Abstract method with optional arguments:
return_type method_name(typeM argM, typeN argN,[typeP? argP, typeQ? argQ]);
Hoặc cú pháp - Các tham số tuỳ chọn với giá trị mặc định:
// Non-static method with optional arguments:
return_type method_name(typeM argM, typeN argN,
[typeP? argP = defaultValueP, typeQ? argQ]){
// statement(s)
}
// Static method with optional arguments:
static return_type method_name(typeM argM, typeN argN,
[typeP? argP = defaultValueP, typeQ? argQ]){
// statement(s)
}
// Abstract method with optional arguments:
return_type method_name(typeM argM, typeN argN,
[typeP? argP = defaultValueP, typeQ? argQ]);
Ví dụ:
method_optional_args_ex1.dart
class MyUtility {
static String concat(String s1, String s2, [String? s3]) {
if (s3 != null) {
return s1 + s2 + s3;
}
return s1 + s2;
}
static double sum(double v1, double v2, [double? v3, double? v4]) {
return v1 + v2 + (v3 ?? 0) + (v4 ?? 0);
}
}
void main() {
String result1 = MyUtility.concat('One', 'Two');
print('result1: $result1');
String result2 = MyUtility.concat('One', 'Two', 'Three');
print('result2: $result2');
double value1 = MyUtility.sum(1, 2, 3, 4);
print('value1: $value1');
double value2 = MyUtility.sum(1, 2, 3);
print('value2: $value2');
double value3 = MyUtility.sum(1, 2);
print('value3: $value3');
}
Output:
result1: OneTwo
result2: OneTwoThree
value1: 10.0
value2: 6.0
value3: 3.0
Ví dụ:
method_optional_args_ex2.dart
class Team {
List<String> members = [];
void addMembers(String member1, [String? member2, String? member3]) {
members.add(member1);
if(member2!= null) {
members.add(member2);
}
if(member3!= null) {
members.add(member3);
}
}
void printAllMembers() {
print(' --- All members: --- ');
for(var member in members) {
print(member);
}
}
}
void main() {
var team = Team(); // Create Team object.
team.addMembers('Tom', 'Jerry');
team.printAllMembers();
}
Output:
--- All members: ---
Tom
Jerry
6. Các tham số tuỳ chọn được đặt tên
Các tham số tuỳ chọn được đặt tên (Optional Named Parameters) là các tham số được đặt trong cặp dấu ngặc { } và là các tham số cuối cùng trong danh sách các tham số. Tất cả các tham số này đều có giá trị mặc định.
Cú pháp:
// Non-static method with optional named parameters:
return_type method_name(typeM argM, typeN argN,
{typeP paramP = defaultValueP, typeQ paramQ = defaultValueQ}){
// statement(s)
}
// Static method with optional arguments:
static return_type method_name(typeM argM, typeN argN,
{typeP paramP = defaultValueP, typeQ paramQ = defaultValueQ}){
// statement(s)
}
// Abstract method with optional arguments:
return_type method_name(typeM argM, typeN argN,
{typeP paramP = defaultValueP, typeQ paramQ = defaultValueQ});
Ví dụ:
method_optional_named_args_ex1.dart
class StringUtility {
static String concatAndTrim(String s1, String s2, {bool trimLeft = true, bool trimRight = true}) {
var s = s1 + s2;
if (trimLeft && trimRight) {
return s.trim();
} else if (trimLeft) {
return s.trimLeft();
} else if (trimRight) {
return s.trimRight();
}
return s;
}
}
void main() {
var s1 = ' One ';
var s2 = ' Two ';
var result1 = StringUtility.concatAndTrim(s1, s2); // trim left and right
print('result1: -->$result1<-- (Trim left and right)');
var result2 = StringUtility.concatAndTrim(s1, s2, trimLeft: false); // trim right only
print('result2: -->$result2<-- (Trim right only)');
var result3 = StringUtility.concatAndTrim(s1, s2, trimRight: false); // trim left only
print('result3: -->$result3<-- (Trim left only)' );
var result4 = StringUtility.concatAndTrim(s1, s2, trimLeft: false, trimRight: false); // no trim
print('result4: -->$result4<!-- (No Trim)');
}
Output:
result1: -->One Two<-- (Trim left and right)
result2: --> One Two<-- (Trim right only)
result3: -->One Two <-- (Trim left only)
result4: --> One Two <!-- (No Trim)
7. Ghi đè một phương thức
Lớp con có thể ghi đè (override) một phương thức của lớp cha nếu các điều kiện sau được thoả mãn:
- Hai phương thức phải cùng tên và có các tham số giống nhau.
- Kiểu trả về của 2 phương thức phải giống nhau hoặc kiểu trả về của phương thức tại lớp con phải là kiểu con của kiểu trả về của phương thức tại lớp cha.
Ví dụ:
- Lớp Cat ghi đè (override) phương thức sayAnything() của lớp Animal.
- @override là một chú thích (annotation), nó là một tuỳ chọn, được đặt phía trước của phương thức ghi đè (tại lớp con). Nếu bạn vô tình đổi tên phương thức này ở lớp cha, trình biên dịch Dart sẽ thông báo lỗi cho bạn.
method_override_ex1.dart
class Animal {
void sayAnything() {
print('<Nothing>');
}
}
class Cat extends Animal {
@override
void sayAnything() { // Override method from parent class.
print('Meow');
}
}
void main() {
Animal animal = Cat();
animal.sayAnything();
}
Output:
Meow
Ví dụ: Sử dụng từ khoá super để gọi tới phương thức của lớp cha.
method_override_ex2.dart
class BoldFormatter {
String formatText(String text) {
return '<b>' + text + '</b>';
}
}
class BoldItalicFormatter extends BoldFormatter {
@override
String formatText(String text) { // Override method from parent class.
var boldText = super.formatText(text); // Call super method.
return '<i>' + boldText + '</i>';
}
}
void main() {
var formatter = BoldItalicFormatter();
var formattedText = formatter.formatText('Hello');
print(formattedText);
}
Output:
<i><b>Hello</b></i>
8. Final method?
Trong ngôn ngữ Java, bạn có thể sử dụng từ khoá final cho một phương thức để đảm bảo rằng các lớp con không thể ghi đè (override) phương thức này. Vậy có gì đó tương tự trong Dart không?
package:meta cung cấp cho bạn @nonVirtual để ngăn chặn việc một lớp con ghi đè một phương thức nào đó của lớp cha. Nó giúp Analyzer (trình phân tích mã) thông báo lỗi nếu bạn vi phạm điều đó, nhưng không có hành động nào khác được thực hiện.
method_final_ex1.dart
import 'package:meta/meta.dart';
class Foo {
@nonVirtual
void bar() {
print("bar from Foo!");
}
}
class SubFoo extends Foo {
@override
void bar() {
print("bar from SubFoo");
}
}
Về cơ bản, bạn sẽ nhận được một cảnh báo (warning) từ Analyzer giống như thế này:
The member 'bar' is declared non-virtual in 'Foo'
and can't be overridden in subclasses.dart(invalid_override_of_non_virtual_member)
- Thêm thư viện vào dự án Dart
Các hướng dẫn lập trình Dart
- Kiểu dữ liệu Boolean trong Dart
- Hướng dẫn và ví dụ hàm trong Dart
- Hướng dẫn và ví dụ Dart Closures
- Interface trong Dart
- Hướng dẫn và ví dụ phương thức trong Dart
- Constructor trong Dart
- Property trong Dart
- Toán tử chấm chấm (..) trong Dart
- Hướng dẫn và ví dụ Dart Generics
- Lập trình Dart với công cụ trực tuyến DartPad
- Cài đặt Dart SDK trên Windows
- Cài đặt Visual Studio Code trên Windows
- Cài đặt Dart Code Extension cho Visual Studio Code
- Cài đặt Dart Plugin cho Android Studio
- Chạy ví dụ Dart đầu tiên của bạn trong Visual Studio Code
- Chạy ví dụ Dart đầu tiên của bạn trong Android Studio
- Dart JSON với thư viện dart:convert
- Hướng dẫn và ví dụ Dart List
- Biến (Variable) trong ngôn ngữ Dart
- Hướng dẫn và ví dụ Dart Map
- Vòng lặp trong Dart
- Xử lý Dart JSON với gói dart_json_mapper
- Trình chuyển đổi mã nguồn (Transpiler) là gì?
- Phân tích XML trong Dart
- Hướng dẫn và ví dụ Dart http
- Hướng dẫn và ví dụ Dart Future
- Các phương thức mở rộng (Extension) trong Dart
- Mixins trong Dart
- Bài thực hành Dart phân tích JSON với gói dart:convert
- Bài thực hành Dart http CRUD
- Từ khoá part và part of trong Dart
- Hướng dẫn và ví dụ Dart Dio
Show More