[TOC]

# 基础类型

# 泛型

​ Dart2中的泛型同C#大部分都相同,可以按C#的用法来使用(只有小部分语法不同)。

1.创建泛型集合的几种方法

//工厂方法创建泛型集合实例,List<T>为Dart核心库中的类
var list=List<String>.from(['a','b']);
//使用可忽略的new关键字
var list=new List<String>.from(['a','b']);
//使用类型推断
var list=['a','b'];
//使用构造函数,这点与C#不同,List<T>算然是抽象类也可new
var list=new List<string>();
list.addAll(['a','b']);
1
2
3
4
5
6
7
8
9

2.自定义泛型类,泛型方法

class Hello{
    void say<T>(){
    }
}
//定义泛型类
class MyList<T>{
}
//带约束的泛型类
class HelloList<T extends Hello>{
}
//多种创建实例的方法
var list=new MyList();
var list=MyList();
var list=new HelloList<Hello>();
var list=HelloList<Hello>();
list.say('a');
list.say(1);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# mixin用法

mixin语法定义了一段代码片段,通过混入类的方式来解决无法多继承的问题,有些类似设计模式中的组合功能;dart中任意一个class都具有mixin的功能,通过mixin关键字替换class来限制常规类仅可作为mixin类型

# 动物类型继承关系例子

class Animal{}
//会飞的接口定义
class IFly{
  void flying(){
    print('flying');
  }
}
//会游泳的接口帝国一
class ISwim{
  void swimming(){
    print('swimming');
  }
}
//会跑的接口定义
class IRun{
  void running(){
    print('running');
  }
}
//会游泳,飞,跑的鸟
class Birds extends Animal implements ISwim,IFly,IRun{
  
  void flying() {
    print('flying');
  }
  
  void running() {
    print('running');
  }
  
  void swimming() {
    print('swimming');
  }
}
//会跑的老虎
class Tiger extends Animal implements IRun{
  
  void running() {
    print('running');
  }
}
//会游泳的鱼
class Fish extends Animal implements ISwim{
  
  void swimming() {
    print('swimming');
  }
}
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
43
44
45
46
47
48
49
50

上面例子我们看到了简答的继承关系,我们实现ISwim,IFly,IRun接口的时必须要实现接口定义的方法,这些看着似乎有点不爽,因为我们在接口中已经实现了对应的方法,在子类中还要重复实现,所以dart引入了mixin来解决这个问题。

# 通过mixin解决多继承的限制

class Animal{}
//会飞的接口定义
class IFly{
  void flying(){
    print('flying');
  }
}
//会游泳的接口帝国一
class ISwim{
  void swimming(){
    print('swimming');
  }
}
//会跑的接口定义
class IRun{
  void running(){
    print('running');
  }
}
//会游泳,飞,跑的鸟
class Birds extends Animal with ISwim,IFly,IRun{
}
//会跑的老虎
class Tiger extends Animal with IRun{
}
//会游泳的鱼
class Fish extends Animal with ISwim{
}
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

我们可以看到通过with替换implements后的代码,是不是比之前更简洁一些,这就是mixin带来的混入机制。

让mixin代码跟规范一点

​ 因为dart中class天生具有mixin的特性,这里我们做一下简单的修改,使mixin的用法更规范一点。

class Animal{}
//会飞的接口定义
mixin IFly{
  void flying(){
    print('flying');
  }
}
//会游泳的接口帝国一
mixin ISwim{
  void swimming(){
    print('swimming');
  }
}
//会跑的接口定义
mixin IRun{
  void running(){
    print('running');
  }
}
//会游泳,飞,跑的鸟
class Birds extends Animal with ISwim,IFly,IRun{
}
//会跑的老虎
class Tiger extends Animal with IRun{
}
//会游泳的鱼
class Fish extends Animal with ISwim{
}
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

# 限制mixin类型的使用范围

​ dart中定义mixin可以通过on关键字来限定使用范围

//定义一个有活的的动物,通过on限制只允许继承Anmial类的子类才可以使用Living
mixin Living on Anmial{
    void isLivling(){
        print('isLiving');
    }
}
//现在我们定义一只机器老虎,但玩具老虎是没有生命的,所以不能使用Living,下面代码会报错:
class ToyTiger with Living,IRun{
}
//下面是一个有生命的老虎,所以可以使用Living
class Tiger extends Animal with Living,IRun{
}
1
2
3
4
5
6
7
8
9
10
11
12

# 有相同成员的mixin类型优先级问题

mixin TestA{
    hello(){
        print('TestA');
    }
}
mixin TestB{
    hello(){
        print('TestB');
    }
}
class TestExtend{
    hello(){
        print('TestExtend');
    }
}
class Test1 extends TestExtend with TestA,TestB{
    hello(){
        print('Test1');
    }
}
class Test2 extends TestExtend with TestA,TestB{
}
void main(){
    Test1().hello(); //Test1
    Test2().hello(); //TestB
}
优先级总结:Test1.Hello() > TestB.Hello() > TestA.Hello() > TestExtend.Hello()
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

# mixin的语法限制

  1. mixin仅作为一个代码片段存在,它不能直接被调用,需要通过with混入其它class后才有意义
  2. mixin可以通过on来限制使用范围
  3. class默认据用mixin的特性,通过使用mixin关键字替换class可以定义一个仅具有mixin功能的代码片段
  4. mixin不能使用extends但可以使用implements。

# 面向对象(类,抽象类,接口,getter/setter访问器)

# 抽象类

​ dart中使用abstract修饰class来定义抽象类,抽象方法为抽象类中无方法体的方法定义,使用extends来继承抽象方法,抽象类不能使用new创建实例。

//抽象类
abstract class Test{
    //抽象方法
    void hello();
}
class TestA extends Test{
  
  void hello () {
  }
}
1
2
3
4
5
6
7
8
9
10
11

# 接口

​ dart中只存在隐式接口,即通过implements引入的类或者抽象类都可作为接口,dart要求子类必须实现接口中定义的除构造函数之外的任意成员变量。

class TestA{
    void hello()
    {
        print('TestA.hello');
    }
}
abstract class TestB{
    hello();
}
//按接口方式实现
class Test1 implements TestA{
  
  void hello () {
      print('Test1.Hello');
  }
}
//按接口方式实现
class Test2 implements TestB{
  
  void hello () {
  	print('Test2.Hello);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

#

# 构造函数

  1. 每个class中都有一个默认无参构造函数

    class Test{}
    //等效如下:
    class Test{
        Test();
    }
    
    1
    2
    3
    4
    5
  2. 要定义多个构造函数必须使用命名构造函数

    class Test{
        Test();
        //不允许存在同名构造函数,即使参数不同也不可以
        //Test(String name);
        //下面定义了两个命名构造函数
        Test.noArg();
        Test.oneArg(String name);
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
  3. 如果一个类定义了一个以上的非默认构造函数,则默认构造函数就不会被定义。

    class Test{
        Test(String name);
    }
    //因为定义了有参构造函数,所以默认构造函数就是失去了意义
    //正确的用法
    new Test('name');
    //错误的用法,因为存在其它构造函数
    Test();
    
    1
    2
    3
    4
    5
    6
    7
    8
  4. 子类必须实现父类中至少一个构造函数

    class TestA{
        Test.noArg();
        Test.OneArg();
    }
    class Test1 extends TestA{
        //因为TestA中没有默认构造函数,所以必须至少实现父类的一个构造函数
        Test1.noArg():super.noArg();
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
  5. 构造函数无法被子类继承

    class TestA{
        Test.noArg();
        Test.OneArg(String name);
    }
    class Test1 extends TestA{
        Test1.noArg():super.noArg();
    }
    //因为构造函数不能不继承,所以无法调用父类的构造函数
    //错误的用法,Test1只实现了noArg构造函数,所以只能调用noArg构造函数
    Test1().oneArg();
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  6. 子类构造函数如果不指定调用父类哪个构造函数,且父类存在默认构造函数,则子类被实例化时一定会调用父类的默认构造函数。

    class TestA{
        Test(){
            print('TestA');
        }
    }
    class Test1 extends TestA{
        Test1(){
            print('Test1');
        }
    }
    //父类的构造函数先执行
    new Tesst1();
    //TestA
    //Test1
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

# 工厂构造函数

  1. 工厂构造函数属于构造函数的一种特殊用法,构造函数不需要返回值,但是工厂构造函数必须返回该类的实例,使用方式同构造函数没有区别。

  2. 工厂构造函数类似设计模式中的工厂方法,但不能使用this关键字有点类似静态方法的限制(实际就是dart的语法糖)。

  3. 需要使用factory来定义工厂构造函数。

    class Test{
      	final String name;
        factory Test(String name){
            print(name);
            return Test._oneArg(name);
        }
        Test._oneArg(this.name);
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

# 函数

  1. dart不支持函数重载,也就是说不能存在相同名称的函数。
  2. dart函数名成必须是以小写字母开头。

# Typedefs(委托)

​ dart中每个函数都是Function类型,函数的传递可以使用Function做参数定义,有了Typedefs后就更方便了。

这是一个将函数做为参数传递的例子

void main() {
  //该代码段运行正常
  Test().acceptFunc(() {
    print('hello world!');
  });
  //该代码段无法正常运行,因为acceptFunc中无法确定参数个数
  Test().acceptFunc((String hello) {
    print(hello);
  });
  //该代码段无法正常运行,因为acceptFunc中无法确定参数个数
  Test().acceptFunc(helloFunc);
}
String helloFunc(String name) {
  print('hello world!');
  return name;
}
class Test {
  //接受一个函数作为参数,但是无法通过参数类型限制接受什么样的函数
  void acceptFunc(Function func) {
    if (func != null) {
      func();
    }
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

从上面代码我们很容易发现Function做参数类型的一些限制,比如我们只希望接受无参的函数。幸好dart提供了解决办法,通过Typedefs我们就可以实现。

通过Typedefs限定函数参数的类型

//定义4个typedef类型
typedef void NoArgType();
typedef void OneArgType(String name);
typedef int NoArgAndResultType();
typedef int OneArgAndResultType(String name);
//定义4个方法
void noArgFunc() {
  print('noArgFunc');
}
void oneArgFunc(String name) {
  print('oneArgFunc');
}
int noArgAndResultFunc(){
  print('noArgAndResultFunc');
  return 0;
}
int oneArgAndResultFunc(String name) {
  print('oneArgAndResultFunc');
  return 0;
}
class Test {
  //定义4个函数分别接受4种类型的typedef
  void acceptNoArgFunc(NoArgType func) {
    if (func != null) {
      func();
    }
  }
  void acceptOneArgFunc(OneArgType func) {
    if (func != null) {
      func('Hello World!');
    }
  }
  void acceptNoArgAndResultFunc(NoArgAndResultType func){
    if(func!=null){
      func();
    }
  }
  void acceptOneArgAndResultFunc(OneArgAndResultType func) {
    if (func != null) {
      func('Hello World!');
    }
  }
}
void main() {
  //无参无返回值函数
  Test().acceptNoArgFunc(() {
    print('hello world!');
  });
  Test().acceptNoArgFunc(noArgFunc);
  Test().acceptNoArgFunc(noArgAndResultFunc);
  //以下是错误的用法
  // Test().acceptNoArgFunc((String name){});
  // Test().acceptNoArgFunc(oneArgFunc);
  // Test().acceptNoArgFunc(oneArgAndResultFunc);
  //无参有返回值函数
  Test().acceptNoArgAndResultFunc((){
    return 0;
  });
  Test().acceptNoArgAndResultFunc(noArgAndResultFunc);
  //以下是错误的用法
  // Test().acceptNoArgAndResultFunc(noArgFunc);
  // Test().acceptNoArgAndResultFunc(oneArgFunc);
  // Test().acceptNoArgAndResultFunc(oneArgAndResultFunc);
  //有参无返回值函数
  Test().acceptOneArgFunc((String name){});
  Test().acceptOneArgFunc(oneArgFunc);
  Test().acceptOneArgFunc(oneArgAndResultFunc);
  // 以下是错误的用法
  // Test().acceptOneArgFunc(noArgFunc);
  // Test().acceptOneArgFunc(noArgAndResultFunc);
  //有参有返回值函数
  Test().acceptOneArgAndResultFunc((String name)=> 0);
  Test().acceptOneArgAndResultFunc(oneArgAndResultFunc);
  //以下是错误的用法
  // Test().acceptOneArgAndResultFunc(noArgFunc);
  // Test().acceptOneArgAndResultFunc(noArgAndResultFunc);
  // Test().acceptOneArgAndResultFunc(oneArgFunc);
}
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86

上面的例子中我们定义4种类型的Typedefs,并分辨演示了每种类型的Typedefs在做参数时的用法,最终总结如下:Typedefs定义的类型对入参的验证比较严格,对于返回值为void类型的定义允许接受非void返回值的函数,反过来则不行。