Dart-Tour2-类

Dart语法样式: https://www.dartlang.org/guides/language/effective-dart/style
语法:https://www.dartlang.org/guides/language/language-tour

概览

  • 重要概念
  • 关键词
  • 注释
  • 变量
  • 内置类型
  • 函数
  • 操作符
  • 流程
  • 异常
  • 泛型
  • 异步
  • 发电机
  • 可调用的类
  • 分离
  • 类型定义
  • 元数据

Dart是一种具有类和基于mixin的继承的面向对象语言。每个对象都是一个类的实例,所有类都来自Object
基于Mixin的继承意味着虽然每个类(除了Object)只有一个超类,但是类体可以在多个类层次结构中重用。

特点:当最左边的操作数为null时,使用?.而不是.避免异常:

构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class {
num x,y;


// 原始写法
Point(num x, num y){
// 该this关键字是指当前实例。
this.x = x;
this.y = y;
}*/

// 使用Dart语法糖
// 在构造体运行之前,为x,y设置语法糖。
Point(this.x, this.y);

}

默认构造函数

如果未声明构造函数,则会为您提供默认构造函数。默认构造函数没有参数,并在超类中调用无参数构造函数。

构造函数不是继承的

子类不从其超类继承构造函数。没有声明构造函数的子类只有默认(无参数,无名称)构造函数。

命名构造函数(多构造函数)

1
2
3
4
5
6
7
8
// 命名构造函数:origin
Point.origin(){
x = 8;
y = 9;
}

// 命名构造函数:withVal
Point.withVal(this.x, this.y);

注意:构造函数不是继承的,这意味着超类的命名构造函数不会被子类继承。如果希望使用超类中定义的命名构造函数创建子类,则必须在子类中实现该构造函数。

一个完整的多构造函数写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class {

num x,y;


// 原始写法
Point(num x, num y){
// 该this关键字是指当前实例。
this.x = x;
this.y = y;
}*/


// 使用Dart语法糖
// 在构造体运行之前,为x,y设置语法糖。
Point(this.x, this.y);

// 命名构造函数:origin
Point.origin(){
x = 8;
y = 9;
}

// 命名构造函数:withVal
Point.withVal(this.x, this.y);

}

调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void classAboutMethod(){

var pointOrigin = Point(34,67);
print(pointOrigin.x);
print(pointOrigin.y);

var pointAbout = Point.origin();
print(pointAbout.x);
print(pointAbout.y);

var pointAboutVal = Point.withVal(11, 21);
print(pointAboutVal.x);
print(pointAboutVal.y);
}

结果为:

1
2
3
4
5
6
34
67
8
9
11
21

调用非默认的超类构造函数

执行顺序如下:

  • 初始化列表
  • 超类的无参数构造函数
  • 主类的无参数构造函数

初始化列表

重定向构造函数

有时构造函数的唯一目的是重定向同一个类中的另一个构造函数重定向构造函数主体是空的,构造函数调用出现在冒号(:)之后。

1
2
3
4
5
6
7
8
9
class {

num x,y;

Point(this.x, this.y);

Point.alongX(num x) : this(x,0);

}

调用:

1
2
3
var pointAlongX = Point.alongX(11);
print(pointAlongX.x);
print(pointAlongX.y);

结果为:

1
2
11
0

常量构造函数

如果你想使你的类生成永远不会更改的对象,则可以使这些对象成为编译时常量。为此,请 定义 const构造函数确保 所有实例变量都是final

1
2
3
4
5
6
7
class ImmutablePoint {
static final ImmutablePoint origin = const ImmutablePoint(0, 0);

final num x, y;

const ImmutablePoint(this.x, this.y);
}

工厂建设者

在不需要总是创建新的实例的情况下可以使用 factory 关键字,例如下面这个例子表示:从缓存中返回对象的构造函数:

注意:工厂构造函数无权访问this。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class {

num x,y;

Point.alongX(num x) : this(x,0);

static final Map<String, Point> _cache = Map<String, Point>{};

factory Point.name(String name){
if (_cache.containsKey(name)) {
return _cache[name];
}else {
final point = Point.alongX(9);
_cache[name] = point;
return point;
}
}
}

调用:

1
2
3
4
5
6
7
var pointName = Point.name('Alice');
var pointName2 = Point.name('Alice');
var pointName3 = Point.name('Bob');
print(pointName.hashCode);
print(pointName2.hashCode);
print(pointName3.hashCode);
assert(pointName == pointName2);

结果:

1
2
3
477440163
477440163
461006651

使用class成员

  • 使用 . 来引用实例变量或方法
1
2
3
var pointOrigin = Point(34,67);
print(pointOrigin.x);
print(pointOrigin.y);
  • 最左边的操作数为null时,使用 ?. 而不是 . 避免异常:
1
2
3
4
5
6
7
8
9
// 如果pointAbout不为空,那么设置x = 4;
pointAbout?.x = 4;
print('pointAbout.x is ${pointAbout.x}');
// 结果:pointAbout.x is 4

pointAbout = null;
pointAbout?.x = 4;
//print('pointAbout.x is ${pointAbout.x}');
// 结果:抛出异常

使用构造函数

一般情况

1
2
3
4
5
6
7
var pointOrigin = Point(34,67);
print(pointOrigin.x);
print(pointOrigin.y);

var pointAbout = Point.origin();
print(pointAbout.x);
print(pointAbout.y);

注意:在Dart2中,new是可选关键字。

常量构造函数

有些类提供常量构造函数。要使用常量构造函数创建编译时常量,请将const关键字放在构造函数名称之前

1
var p = const ImmutablePoint(2, 2);

如果构造2个相同编译时常量,那么结果是:产生同一个规范的实例:

1
2
3
4
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);

assert(identical(a, b)); // They are the same instance!

在常量上下文中,您可以省略const构造函数或文字之前的内容。例如,查看此代码,该代码创建一个const映射:

1
2
3
4
const pointAndLine = const {
'point': const [const ImmutablePoint(0, 0)],
'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
};

那么,除了第一次出现的const外其他所有的const关键字都可以省略。简写为:

1
2
3
4
5
// Only one const, which establishes the constant context.
const pointAndLine = {
'point': [ImmutablePoint(0, 0)],
'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)],
};

如果常量构造函数在常量上下文之外并且在没有const的情况下调用,则会创建一个常量对象:

1
2
3
4
var a = const ImmutablePoint(1, 1); // Creates a constant
var b = ImmutablePoint(1, 1); // Does NOT create a constant

assert(!identical(a, b)); // NOT the same instance!

获取对象的类型

要在运行时获取对象的类型,可以使用ObjectruntimeType属性,该属性返回一个Type对象

1
2
var pointAboutVal = Point.withVal(11, 21);
print('the type of pointAboutVal is ${pointAboutVal.runtimeType}');

结果为:

1
the type of pointAboutVal is Point

实例变量

以下是 声明 实例变量的方法:

1
2
3
4
5
class  {
num x; // Declare instance variable x, initially null.
num y; // Declare y, initially null.
num z = 0; // Declare z, initially 0.
}

所有未初始化的实例变量都具有该值null

所有实例变量都生成一个隐式getter方法。非final实例变量也会生成隐式setter方法。有关详细信息,请参阅Getters和setter。

1
2
3
4
5
6
7
8
9
10
11
class Point {
num x;
num y;
}

void main() {
var point = Point();
point.x = 4; // Use the setter method for x.
assert(point.x == 4); // Use the getter method for x.
assert(point.y == null); // Values default to null.
}

如果初始化声明它的实例变量(而不是构造函数或方法),则在创建实例时设置该值,该实例在构造函数及其初始化列表执行之前。

方法

方法是为对象提供行为的函数。

实例方法

对象的实例方法可以访问实例变量和this。

getters and setters

抽象方法

实例、getter和setter方法可以是抽象的,定义一个接口,但将其实现留给其他类。抽象方法只能存在于抽象类中。

1
2
3
4
5
6
7
8
9
10
11
abstract class Doer {
大专栏  Dart-Tour2-类"line"> // Define instance variables and methods...

void doSomething(); // Define an abstract method.
}

class EffectiveDoer extends Doer {
void doSomething() {
// Provide an implementation, so the method is not abstract here...
}
}

抽象类(abstract)

使用abstract修饰符定义抽象类 - 无法实例化的类。抽象类对于定义接口非常有用,通常还有一些实现。如果希望抽象类看起来是可实例化的,请定义工厂构造函数

1
2
3
4
5
abstract class Doer {
// Define instance variables and methods...

void doSomething(); // Define an abstract method.
}

DartJava 关于 抽象类 的区别:

  • Dart:由abstract关键字修饰的类就是抽象类,由关键字决定。
  • Java:只要一个类中有未实现的方法,这个方法就是抽象类,由方法决定。

隐式接口(interfaces)

  • 每个类隐式的定义了一个接口,含有类的所有实例和它实现的所有接口。如果你想创建一个支持类 B 的 API 的类 A,但又不想继承类 B ,那么,类 A 应该实现类 B 的接口。

  • 一个类实现一个或更多接口通过用 implements 子句声明,然后提供 API 接口要求。例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// A person. The implicit interface contains greet().
class Person {
// In the interface, but visible only in this library.
final _name;

// Not in the interface, since this is a constructor.
Person(this._name);

// In the interface.
String greet(String who) => 'Hello, $who. I am $_name.';
}

// An implementation of the Person interface.
class Impostor implements Person {
get _name => '';

String greet(String who) => 'Hi $who. Do you know who I am?';
}

String greetBob(Person person) => person.greet('Bob');

void main() {
print(greetBob(Person('Kathy')));
print(greetBob(Impostor()));
}

这里是具体说明一个类实现多个接口的例子:

1
2
3
class Point implements Comparable, Location {
// ...
}

特点:
这里的interface接口和java不同在于:不需要定义interface任何class都可以被implements

继承类(extends)

同Java

重写成员(@override)

同Java

注意:要在类型安全的代码中缩小 方法参数实例变量类型,可以使用covariant关键字。

例子:

1
2
3
4
5
6
7
8
9
class Animal {
void chase(Animal x) { ... }
}

class Mouse extends Animal { ... }

class Cat extends Animal {
void chase(covariant Mouse x) { ... }
}

虽然此示例显示covariant子类型中使用,但covariant关键字可以放在超类子类方法中。通常,超类方法是放置它的最佳位置。该covariant关键字适用于单个参数,并且在setter和字段上也受支持。

重写操作符

您可以覆盖下表中显示的运算符。

说明:表达e1 != e2!(e1 == e2)的语法糖。

一个覆盖+-运算符的类的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Vector{
final x,y;

Vector(this.x, this.y);

Vector operator +(Vector v) => Vector(x + v.x,y + v.y);
Vector operator -(Vector v) => Vector(x - v.x,y - v.y);

}

final v = Vector(2,2);
print('x is ${v.x}, y is ${v.y}');

final w = Vector(4,5);
print('x is ${w.x}, y is ${w.y}');

var vw = v + w;
print('x is ${vw.x}, y is ${vw.y}');

结果为:

1
2
3
x is 2, y is 2
x is 4, y is 5
x is 6, y is 7

noSuchMethod()

可以通过重写noSuchMethod()可以发现或者对在代码中尝试使用不存在的方法或者实例变量作出反应。

1
2
3
4
5
6
7
8
9
class A {
// Unless you override noSuchMethod, using a
// non-existent member results in a NoSuchMethodError.

void noSuchMethod(Invocation invocation) {
print('You tried to use a non-existent member: ' +
'${invocation.memberName}');
}
}

你不能调用未实现的方法,除非以下一个是真实的:

  • 接收器具有静态类型dynamic。

  • 接收器有一个静态类型,它定义了未实现的方法(抽象是OK),接收器的动态类型的实现与类noSuchMethod() 中的实现不同Object。

枚举类型

枚举类型(通常称为枚举或枚举)是一种特殊类,用于表示固定数量常量值

使用enum关键字声明枚举类型:

1
enum Color { red, green, black }

枚举中的每个值都有一个index,从0开始。如:

1
assert(Color.black.index == 2);

要获取枚举中所有值的列表,请使用枚举values常量。如:

1
2
3
4
List<Color> colors = Color.values;
for (var x in colors) {
print('当前的索引为: ${x.index} ,对应着的值为:${x}');
}

结果为:

1
2
3
当前的索引为: 0 ,对应着的值为:Color.red
当前的索引为: 1 ,对应着的值为:Color.blue
当前的索引为: 2 ,对应着的值为:Color.black

switch用法

1
2
3
4
5
6
7
8
9
10
11
12
13
void doSwitch() {
var colorSingel = Color.black;
switch (colorSingel) {
case Color.black:
break;
case Color.blue:
break;
case Color.red:
break;
default:
print('');
}
}

向类添加功能:mixin

mixins是一种在多个类层次结构重用类代码的方法。可以简单的理解为: mixin修饰的类中定义了多个类公用变量方法

mix混合的意思in在……里面。那么mixin可以理解为:可以混合在指定类里面公用代码块。用mixin关键字代替class关键字来实现一个可扩展的Object类,且无需声明构造函数 :

mixin本身可以抽象的,可以定义各种方法属性,也可以是抽象的,等后续类去实现。

mixin使用

要使用 mixin,请使用with关键字后跟一个或多个 mixin关键字修饰的类。以下示例显示了使用有mixins修饰的类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mixin TestMixin {
void test() {
print('test');
}
int testInt = 1;
void test2();
}

class Test with TestMixin {

test2() {
print('test2');
}
}

void main() {
Test().test(); // test
print(Test().testInt); // 1
Test().test2(); // test2
}

注意:mixinDart 2.1中引入的对关键字的支持。

mixin与on关键字:

要指定只有某些类型可以使用mixin——使用on来指定所需的超类,这样你的mixin可以调用一个它自身没有定义的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class BaseObject {
void method() {
print('call method');
}
}
mixin TestMixin on BaseObject{
void test() {
print('test');
}
int testInt = 1;
void test2() {
method();
}
}

class Test extends BaseObject with TestMixin {
}

void main() {
Test().test(); // test
print(Test().testInt); // 1
Test().test2(); // call method
}

当使用on关键字后,则表示该mixin修饰的类只能在那个类的子类使用了。

多个mixin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
mixin TestMixin1 on BaseContext{

var intValue = 1;

void mix1() => print('this is from textMixin1');

void printAnother() => sayHello();

}

mixin TestMixin2 on BaseContext{
var intValue = 2;
void mix2() => print('this is from testMixin2');

void printBye() => sayGoodbye();
}

class BaseContext{

void sayHello() => print("BaseContext say hello");

void sayGoodbye() => print('BaseContext say byebye');

}

class TestClass extends BaseContext with TestMixin1,TestMixin2{
@override
void mix2() {
// TODO: implement mix2
super.mix2();
print('this msg is from TestClass!');
}
}

void main(){
TestClass().mix2(); // this is from testMixin2 this msg is from TestClass!
TestClass().mix1(); // this is from textMixin1
TestClass().sayGoodbye(); // BaseContext say byebye
TestClass().printAnother(); // BaseContext say hello
TestClass().printBye(); // BaseContext say byebye
print(TestClass().intValue); // 2
}

TestMixin1TestMixin2中都声明了一个intValue变量,但是值不同。在class TestClass extends BaseContext with TestMixin1,TestMixin2的顺序中,得到的结果是:

1
2

即:TestMixin2 中初始化的值。

那么改变一下with的顺序呢?class TestClass extends BaseContext with TestMixin2,TestMixin1,结果是:

1
1

由此可见:取值的是最后一个with的类中的值。

类变量和方法

使用static关键字实现类范围的变量和方法。

静态变量

静态变量(类变量)对于类范围状态常量很有用:

1
2
3
4
5
6
7
8
class Queue {
static const initialCapacity = 16;
// ···
}

void main() {
assert(Queue.initialCapacity == 16);
}

注意:

  • 静态变量在使用之前不会初始化。
  • 遵循样式指南建议,优先使用lowerCamelCase作为常量名称

静态方法

1
2
3
4
5
6
7
8
9
10
class Point {
num x, y;
Point(this.x, this.y);

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);
}
}

注意

  • 对于常用或广泛使用的实用程序和功能,请考虑使用顶级函数而不是静态方法。

您可以使用静态方法作为编译时常量。例如,您可以将静态方法作为参数传递给常量构造函数。

猜你喜欢

转载自www.cnblogs.com/lijianming180/p/12099654.html