前言
笔者在之前已经写了3篇Dart的基础文章了。
Dart 基础 (一)
Dart 基础 (二)
Dart 基础 (三)
笔者在本文中主要会分享:类、实例变量、构造方法、命名构造方法、实例方法、静态变量、静态方法、set、get 方法、extends
、implements
、mixin
、abstract
、override
相关的内容。
1.类
下边笔者先以Point 类为例,分享下关于实例变量
,构造方法
,命名构造方法
,实例方法
、静态方法
、 静态变量
、set get方法
的内容。
Dart 是一种面向对象的编程语言,同时支持基于 mixin 的继承机制。mixin相关的内容会在下文解释。每个对象都是一个类的实例,所有的类都继承于 Object。 基于 Mixin 的继承 意味着每个类(Object 除外) 都只有一个超类,一个类的代码可以在其他 多个类继承中重复使用。
使用 new 关键字和构造方法来创建新的对象。 构造方法名字可以为 ClassName 或者 ClassName.identifier。
在Dart2.0的时候,创建新的对象的时候,new 关键字是可选的。当前Dart最新版本是2.4.0,2019-06-27 Dart开发团队发布2.4.0版本Dart。 Dart change log
1.1 实例变量
class Point {
// 实例变量
num x;
num y;
}
1.2 构造方法:构造方法 定义一个和类名一样的方法
// 构造方法 定义一个和类名一样的方法
Point(num x, num y) {
// this 关键字指当前的实例
this.x = x;
this.y = y;
}
// 由于把构造方法参数赋值给实例变量的场景太常见了, Dart 提供了一个语法糖来简化这个操作
// Point(this.x, this.y);
1.3 命名构造方法
// 命名构造方法
Point.fromJson(Map json) {
// 只有当名字冲突的时候才使用 this。否则的话, Dart 代码风格样式推荐忽略 this。
x = json['x'];
y = json['y'];
}
Point.namedConstructor(Map json){
x = json['x'];
y = json['y'];
}
命名构造方法使用场景有:模型类中解析数据场景。
举个简单例子:如返回一个列表数据的情况,返回数据可能是是一个包着多个字典的数组,那么,处理相应数据的时候,需要对数据进行相应的解析。解析的过程就可能用到命名构造方法。把一个个字典当做实例,提取出来。
[
{
"name":"QiShare1",
"age":"1"
},
{
"name":"QiShare2",
"age":"1"
},
{
"name":"QiShare3",
"age":"1"
},
{
"name":"QiShare4",
"age":"1"
},
{
"name":"QiShare5",
"age":"1"
},
{
"name":"QiShare6",
"age":"1"
},
{
"name":"QiShare7",
"age":"1"
},
]
1.4 实例方法
// 实例方法
num distanceTo(Point other) {
var dx = x - other.x;
var dy = y - other.y;
return sqrt(dx*dx + dy*dy);
}
1.5 静态方法
使用static关键字修饰的方法为静态方法,相当于类方法。使用类名可以直接调用。
// 静态方法
static num distanceBetween(Point a, Point b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
return sqrt(dx * dx + dy * dy);
}
void classDemo() {
var jsonData = jsonDecode('{"x":2, "y":2}');
// Create a Point using Point().
var p1 = new Point(1, 1);
print('p1点x坐标:${p1.x}');
print('p1点y坐标:${p1.y}');
// Create a Point using Point.fromJson().
var p2 = new Point.fromJson(jsonData);
print('p2点x坐标:${p2.x}');
print('p2点y坐标:${p2.y}');
num distance = p2.distanceTo(p1);
print('p1到p2的距离: $distance');
Map jsonData3 = {
'x': 3,
'y': 3,
};
Point p3 = Point.namedConstructor(jsonData3);
print('p3点x坐标:${p3.x}');
print('p3点y坐标:${p3.y}');
num distance12 = Point.distanceBetween(p1, p2);
print('p1和p2之间的距离 $distance12');
}
输出内容
flutter: p1点x坐标:1
flutter: p1点y坐标:1
flutter: p2点x坐标:2
flutter: p2点y坐标:2
flutter: p1到p2的距离: 1.4142135623730951
flutter: p3点x坐标:3
flutter: p3点y坐标:3
flutter: p1和p2之间的距离 1.4142135623730951
1.6 静态变量
静态变量对于类级别的状态是非常有用的,笔者对这句话的理解是:静态变量可以由类名直接调用。
class Color {
static const red =
const Color('red'); // A constant static variable.
final String name; // An instance variable.
const Color(this.name); // A constant constructor.
}
使用方式
String colorName = Color.red.name;
print('colorName:$colorName');
输出内容
colorName:red`
1.7 set get 方法
下边笔者举了一个类Rectangle的left、top、width、height的Set、Get方法的例子。
class Rectangle {
num left;
num top;
num width;
num height;
Rectangle(this.left, this.top, this.width, this.height);
// Define two calculated properties: right and bottom.
num get right => left + width;
set right(num value) => left = value - width;
num get bottom => top + height;
set bottom(num value) => top = value - height;
}
使用方式
Rectangle rectangel = Rectangle(0, 0, 375, 667);
print('rectangel.left:');
print(rectangel.left);
print('rectangel.right:');
print(rectangel.right);
print('rectangel.width:');
print(rectangel.width);
print('rectangel.height:');
print(rectangel.height);
print('rectangel.right:');
print(rectangel.right);
print('rectangel.bottom:');
print(rectangel.bottom);
输出结果:
flutter: rectangel.left:
flutter: 0
flutter: rectangel.right:
flutter: 375
flutter: rectangel.width:
flutter: 375
flutter: rectangel.height:
flutter: 667
flutter: rectangel.right:
flutter: 375
flutter: rectangel.bottom:
flutter: 667
2. extends 与 implements
extends
关键字extends 用于继承父类的实例变量及方法等。Dart 只支持单继承。
implements
Every class implicitly defines an interface containing all the instance members of the class and of any interfaces it implements. If you want to create a class A that supports class B’s API without inheriting B’s implementation, class A should implement the B interface.
每个类都隐式地声明了一个包含所有的实例变量和类已经实现的接口。
如果你想创建一个类A,没有继承类B,但是类A可访问类B的API,那么类A 应该实现类B的接口。
上边的内容,结合着下边的例子,笔者的理解是:Chicken隐式地声明了Animal 的实例变量,和类Animal 已经实现的方法。Chicken支持在没有继承类Animal的情况下,可访问类B的API。
一个类可以implements 多个类的API,所以implements算是一种变向实现多继承的方式。
class Animal {
String name;
void ability() {
print('Animal 的能力');
}
}
class Bird extends Animal {
void ability(){
print('bird can fly');
}
}
class Fish extends Animal {
void ability(){
print('fish can swim');
}
}
class Dog extends Animal {
void ability(){
print('dog can bark');
}
}
class Chicken implements Animal {
String name;
void ability() {
print('chicken can lay eggs');
}
}
调用如上代码的方式及相应输出结果如下:
Dog dog = Dog();
dog.ability();
Fish fish = Fish();
fish.ability();
Bird bird = Bird();
bird.ability();
Chicken chicken = Chicken();
chicken.ability();
// 输出结果:
flutter: dog can bark
flutter: fish can swim
flutter: bird can fly
flutter: chicken can lay eggs
3. mixin
Mixins 是一种在多类继承中重用一个类代码的方法。笔者的理解是,mixin相当于是一个工具类,使用
with
关键字使用了mixin的类,就可以使用mixin中的代码。
Mixins are a way of reusing a class’s code in multiple class hierarchies.
To use a mixin, use the with keyword followed by one or more mixin names. The following example shows two classes that use mixins:
Mixin 是一种在多个类中重用某些代码的方式。
使用mixin ,需使用with
关键字,with后边跟mixin的名,with 后边可以跟多个mixin名字,及可以同时使用多个mixin中的代码。下边笔者举了一个开发者学习基础语言的例子。笔者定义了一个Developer的mixin,如果是iOS 开发者需要先学习C语言基础,如果是Android 开发者,需要先学习Java语言,如果是Flutter 开发者,需要先学习Dart 语言。
mixin Developer {
bool isIOS = false;
bool isAndroid = false;
bool isFlutter = false;
// 需要学习的基础语言
void needLearnBaseProgram () {
if (isIOS) {
print('Need Learn C Firstly');
} else if (isAndroid) {
print('Need Learn Java Firstly');
} else if (isFlutter) {
print('Need Learn Dart Firstly');
} else {
print('May be need Learn Other Language');
}
}
}
class FlutterDeveloper with Developer {
String name;
FlutterDeveloper(String name) {
isFlutter = true;
this.name = name;
}
}
使用的相关代码:
FlutterDeveloper flutterDeveloper = FlutterDeveloper('FlutterEnginerName');
flutterDeveloper.needLearnBaseProgram();
// 输出结果: flutter: Need Learn Dart Firstly
注意事项: 当在if else 场景下使用 bool 类型变量的时候,需要注意bool变量是否赋值过了,否则会有类似如下的异常信息。
flutter: The following assertion was thrown while handling a gesture:
flutter: Failed assertion: boolean expression must not be null
4. abstract
使用 abstract 修饰的类 记为抽象类。抽象类用于定义接口 及部分实现。
笔者举了如下例子:
创建了People 类,并且声明了 String skinColor();的抽象方法,创建并实现了 void ability() 方法;
abstract class People {
String skinColor();
void ability() {
print('All can Communicate');
}
}
class YellowPeople extends People {
@override
String skinColor() {
String color = 'Yellow';
print(color);
return color;
}
}
class BlackPeople extends People {
@override
skinColor() {
String color = 'black';
print(color);
return color;
}
}
class WhitePeople extends People {
@override
skinColor() {
String color = 'White';
print(color);
return color;
}
}
下边是使用示例,及相应的输出结果。
YellowPeople yellowPeople = YellowPeople();
yellowPeople.ability();
yellowPeople.skinColor();
WhitePeople whitePeople = WhitePeople();
whitePeople.ability();
whitePeople.skinColor();
BlackPeople blackPeople = BlackPeople();
blackPeople.ability();
blackPeople.skinColor();
// 输出结果:
flutter: All can Communicate
flutter: Yellow
flutter: All can Communicate
flutter: White
flutter: All can Communicate
flutter: black
- 抽象类不能创建实例。
- 抽象方法为没有方法体的方法。只有抽象类中可以写抽象方法,其他普通类不可以。
- 例:如果BlackPeople的skinColor 没有方法体即没有实现,则会报错如下:'skinColor' must have a method body because 'BlackPeople' isn't abstract.
Try making 'BlackPeople' abstract, or adding a body to 'skinColor'.
- 例:如果BlackPeople的skinColor 没有方法体即没有实现,则会报错如下:'skinColor' must have a method body because 'BlackPeople' isn't abstract.
- 继承了抽象类的子类必须实现抽象方法
- 以WhitePeople 为例,如果不实现skinColor 方法会报出如下错误:
- Missing concrete implementation of People.skinColor.
- Try implementing the missing method, or make the class abstract.
- 以WhitePeople 为例,如果不实现skinColor 方法会报出如下错误:
5. override
"5.1 override 运算符"及override toString
这里笔者对override 运算符添加了引号。至于原因,等大家看完了下边的内容之后,便会了解笔者的用意。下文提到的override和重写是一个意思。
先看下运算符重写的示例代码:
Vector 类,重写了+ 运算符和减运算符,以达到Vector可以直接进行加减的目的。笔者还重写了Vector类的toString 方法,便于查看Vector的x、y值。
class Vector {
final int x;
final int y;
const Vector(this.x, this.y);
Vector operator +(Vector v) {
return Vector(x + v.x, y + v.y);
}
Vector operator -(Vector v) {
return Vector(x - v.x, y - v.y);
}
@override
String toString() {
return 'runtimeType:' + this.runtimeType.toString() + ',x:' + x.toString() +',y:' + y.toString();
}
}
使用Vector的+、-运算符,及重写toString后,使用Vector的示例代码及输出结果如下:
Vector v1 = Vector(1, 1);
Vector v2 = Vector(2, 2);
Vector v3 = v1 + v2;
Vector v0 = v2 - v1;
print(v0);
print(v3);
// 输出结果:
flutter: runtimeType:Vector,x:1,y:1
flutter: runtimeType:Vector,x:3,y:3
重写toString的效果是:可控制print的对象的内容及格式。这一点便于非调试环境下查看一些具体错误信息。
上文笔者提到了重写运算符是加引号的原因如下:在笔者看来,运算符的重写有点跑题了。重写toString才算是重写。重写的toString的返回值、方法名和参数和父类Object都一样。如大家有不同理解,欢迎讨论。