这是 Dart 入门教程第四篇,学习 Dart 中类的使用。
构造函数
要构造类,这就需要构造函数,Dart 的构造函数和其他语言是一样的。但 Dart 有一个构造函数语法糖,这是其他语言没有的。
class Point {
int x;
int y;
// 使用构造函数语法糖
Point(this.x, this.y);
//使用上面语法糖取代传统写法
Point(int x, int y){
this.x = x;
this.y = y;
};
}
void main() {
Point point = Point(10, 20);
print('x: ${point.x}, y: ${point.y}'); // 输出:x: 10, y: 20
}
和其他语言还有一个区别,因为 Dart 不支持函数重载,所以如果我们想创建多个构造函数,这得用到命名构造函数。
class Rectangle {
int width;
int height;
// 默认构造函数
Rectangle(this.width, this.height);
// 命名构造函数
Rectangle.square(int side) {
width = side;
height = side;
}
}
void main() {
Rectangle rect1 = Rectangle(10, 20);
print('rect1 - width: ${rect1.width}, height: ${rect1.height}'); // 输出:rect1 - width: 10, height: 20
Rectangle rect2 = Rectangle.square(15);
print('rect2 - width: ${rect2.width}, height: ${rect2.height}'); // 输出:rect2 - width: 15, height: 15
}
命名构造函数用的非常多,最常见就是实体类的转换,我在Flutter 中用 Dart 解析 JSON 对象、数组、嵌套对象等数据中有分享工具,可以将 json
字符串转成实体类,工具就生成了多个构造函数,其中也包括命名构造函数。
class Autogenerated {
int? stationId;
String? stationName;
Autogenerated({this.stationId, this.stationName});
Autogenerated.fromJson(Map<String, dynamic> json) {
stationId = json['stationId'];
stationName = json['stationName'];
}
}
初始化列表
我之前学习 Dart 时,这初始化列表直接就跳过了,以为没什么用。结果翻一些开源控件的源码时,总是看不懂。加上自己如果写自定义控件,初始化列表是必学知识点。
初始化列表的特征如下,在方法参数后面使用 :
。
class Circle {
final double radius;
final String color;
// 使用初始化列表为 final 属性设置默认值
Circle({double radius, String color})
: this.radius = radius ?? 10.0,
this.color = color ?? 'red';
}
这里要注意,就我看到的情况,初始化列表 99% 和 final
搭配使用,而且使用的场景都是为了给 final
定义的属性设置初始默认值。
final
定义的属性是不可变的,那如果定义时就给它设置默认值,当传入真正的设置值时,并不会赋值修改成功,因为 final
定义的属性是不可变的。
比如说,我将上面代码修改成如下,尽管构造时给 color
传入了颜色,但并未生效,打印出来的还是默认值,因为 final
定义的属性不能被二次修改。
class Circle {
final double radius = 1;
final String color = 'red';
// 使用初始化列表为 final 属性设置默认值
Circle({double? radius, String? color});
}
void main() {
Circle circle1 = Circle(radius: 5, color: 'blue');
print(circle1.color); // red
}
所以上面这种给 final
定义属性设置默认值的方式不可取,会采用初始化列表。
class Circle {
final double radius;
final String color;
// 使用初始化列表为 final 属性设置默认值
Circle({double? radius, String? color})
: this.radius = radius ?? 10.0,
this.color = color ?? 'red';
}
void main() {
Circle circle1 = Circle(radius: 5, color: 'blue');
print(circle1.color); // blue
}
通过 this.radius = radius ?? 10.0
就能实现一种比较好的效果,如果传入的值是空,则使用默认值,如果不是空,则赋值传入的值。
那上面的写法可以简写吗?不使用初始化列表,使用下面方法也可以。
class Circle {
final double radius;
final String color;
// 使用初始化列表为 final 属性设置默认值
Circle({this.radius = 10.0, this.color = 'red'});
}
void main() {
Circle circle1 = Circle(radius: 5,color: 'blue');
print(circle1.color); // blue
}
但上面的方案有缺点,如果默认值不是一个具体的值,而是表达式,这就不行了。比如我换乘表达式,就会报错提醒:
Circle({this.radius = 10.0, this.color = DateTime.now().toString()});
但使用初始化列表是没问题的,所以优势就在这里。
Circle({double? radius, String? color})
: this.radius = radius ?? 10.0,
this.color = color ?? DateTime.now().toString();
工厂构造函数
工厂构造函数主要用来创建缓存的对象实例,避免创建多个相同对象,或者可以用来根据条件创建不同类型的对象。下面是从缓存中获取对象实例的代码
abstract class Animal {
void makeSound();
}
class Dog implements Animal {
@override
void makeSound() {
print('Woof!');
}
}
class Cat implements Animal {
@override
void makeSound() {
print('Meow!');
}
}
class AnimalFactory {
// 缓存已创建的实例
static final Map<String, Animal> _cache = {};
// 工厂构造函数
factory AnimalFactory(String animalType) {
if (_cache.containsKey(animalType)) {
return _cache[animalType];
} else {
Animal animal;
switch (animalType) {
case 'Dog':
animal = Dog();
break;
case 'Cat':
animal = Cat();
break;
default:
throw Exception('Unknown animal type: $animalType');
}
_cache[animalType] = animal;
return animal;
}
}
}
void main() {
Animal animal1 = AnimalFactory('Dog');
animal1.makeSound(); // 输出:Woof!
Animal animal2 = AnimalFactory('Dog');
animal2.makeSound(); // 输出:Woof!
print(identical(animal1, animal2)); // 输出:true
}
抽象类
Dart 和 JAVA 类有一个很重要区别是 Dart 没有接口,Dart 只有抽象类。它不能被实例化,只能被其他类继承。抽象类中可以定义抽象方法,这些方法没有具体的实现,需要由继承抽象类的子类来实现。并且某个类继承自抽象类后,必须实现抽象类的抽象方法。
// 抽象类 Shape abstract class Shape { // 抽象方法,用于计算形状的面积 double getArea(); } // 实现抽象类的子类:矩形 class Rectangle extends Shape { final double width; final double height; Rectangle(this.width, this.height); @override double getArea() { return width * height; } }
虽然 Dart 中没有接口,但它有implements
关键字,只不过不常用,因为使用 implements
后,必须要实现类里所有方法,那如果有的方法已经实现,还得重新再写一遍。比如像下面案例的 methodA
,本身已经实现,结果被继承后,又得再重新实现,因为使用 implements
后,必须要实现类里所有方法。
class AB { void methodA() { print("Method A"); } void methodB() {} } class C { void methodC() {} } class MyClass implements AB, C { @override void methodA() { print("MyClass: Method A"); } @override void methodB() { print("MyClass: Method B"); } @override void methodC() { print("MyClass: Method C"); } }
为了解决上面的问题,大部分情况不用 implements
,而是用 with
混入。将上面代码修改如下:
mixin AB { void methodA() { print("Method A"); } void methodB() {} } mixin C { void methodC() {} } class MyClass with AB, C { @override void methodB() { print("MyClass: Method B"); } @override void methodC() { print("MyClass: Method C"); } }
OK,以上就是 Dart 类的知识点,下篇分享 Dart 中异步和同步的实现,也是 Dart 入门最后一篇文章。
本文由老郭种树原创,转载请注明:https://guozh.net/dart-getting-started-tutorial-class/