Dart语言(二)--对象的构建

以下内容为自学笔记,若有幸被大神看到,望指正其不准,补充其不足。万分感谢!!!

一、CLasses 类中基本内容

  1. Dart是面向对象的编程语言,支持基于mixin的集成机制。
  2. 可以通过构造函数来创建新的对象。可使用new关键字,也可不使用。构造函数名字可以为ClassNameClassName.identifier
  var list1 = new List();
  var list2 = List();
  var list3 = List.from(Iterable.empty());
  1. Dart语言的类中的构造函数可分以下形式:

    • 默认构造函数
    • 命名构造函数
    • 超类构造函数
    • 重定向构造函数
    • 工厂方法构造函数
  2. Dart语言中类分为:

    • 抽象类 Abstract classes
    • 隐式接口 Implicit interfaces
    • 扩展类 Extending a class
    • 枚举类型 Enumerated types
  3. Dart语言的类中包含变量和函数

二、构造函数

1、默认构造函数

  1. 如果你没有定义构造函数,则会有个默认构造函数。默认构造函数没有参数,并且会调用超类的没有参数的构造函数。
  2. 子类不会继承超类的构造函数。子类如果没有定义构造函数,则只有一个默认构造函数(没有名字没有参数)。

2、常规构造函数

使用 new 关键字和构造函数来创建新的对象

//java中写法
class Point1 {
  num x;
  num y;

  Point1(num x, num y) {
    this.x = x;
    this.y = y;
  }
}

//dart建议写法
class Point2 {
  num x;
  num y;
  Point2(this.x, this.y);
}

main(){
    var p1 = new Point1(2, 2);
    var p2 = new Point2(2, 2);
}

3、常量构造函数

a、const和final的区别

  1. final修饰的变量只能初始化一次,但不要求赋的值为编译时常量;而const要求在声明时就初始化,并且初始化必须为编译时常量。
    class Test1{
    	//const修饰成员变量时必须与static同时使用,编译时常量即在编译时就确定了值,
    	//bool,字符串,数字,list的字面量形式都是编译时常量
    	static const a = true;
    	static const int b = 1;
    	static const c = '字符串';
    	static const b = [1,2,3];
    	static const e = b  + 2;//其中b也要是const常量
    }
    
  2. final是惰性初始化,即在运行时第一次使用前才初始化。
    class Test{
    	//可以通过以下两种方式初始化,但都是在运行时才会执行
    	final a ;
    	final b = 1;//可直接初始化
    	//可以通过下面方式初始化
    	Test(this.a){}
    	Test(a) : this.a= a{}
    }
    
  3. const 可以修饰变量,也可以修饰值,而 final只用来修饰变量。
    void test2(){
    	//const修饰局部变量时,直接使用关键字即可。
    	//const既可以修饰变量,也可以修饰常量。
    	const list1 = [1,2,3];
    	const list2 = const [1,2,3];
    	// error, 因为new DateTime.now()不是编译时常量
    //	const list3 = const [new DateTime.now(), 2, 3]; 
    	
    	//final只能修饰变量
     	final list6 = [1, 2, 3];
    //	final list7 = final [1, 2, 3]; //error
    	final list8 = const [1, 2, 3];
    	
    	//const无论修饰变量还是值,后边都必须接编译时常量
     	var x1 = 5;
    //	const list4 = const[x1]; //error,x1不是const
    	const x2 = 5;
    	const list5 = const[x2];
    	// error, 因为new DateTime.now()不是const
    //	final list9 = const [new DateTime.now(), 2, 3];
    }
    

总结:const 修饰值的时候,要求值必需是常量或由常量组成。var、final等在左边定义变量时,并不关心右边是不是常量。但如果右边用了const,那么不管左边是什么要求,右边都必需是常量。

b、常量构造函数

  1. 常量构造函数可以提供一个状态不变的对象,此对象为编译时常量,要使用常量构造函数只需要用 const 替代 new 即可:
  2. 两个一样的编译时常量其实是同一个对象:
  3. 定义一个 const 构造函数,类的所有成员变量都要为 finalconst修饰的。
  4. 可以使用new调用const构造函数,但这样就不能使用const声明变量了。
  5. const 构造函数也不能有函数体。
class Point2 {
  final num x;
  final num y;
  static const num z = 9;
  
  const Point2(this.x, this.y);
}
main(){
    var p1 = const  Point2(2, 2);//ok
    const p2 = const  Point2(2, 2);//ok
 //   const p3 = new Point2(2,2);//error
    final p4 = const Point2(2,2);//ok
    final p5 = new Point2(2,2);//ok
    assert(identical(p1, p2));//判断是否是同一个实例对象 true
    assert(identical(p1, p4));//判断是否是同一个实例对象 true
    assert(identical(p1, p5));//判断是否是同一个实例对象 false
}

4、命名构造函数

  1. Dart语言不支持方法重载。构造函数的重载也不支持,但支持命名构造函数。函数格式:ClassName.identifier,类名.标识符(即方法名)。
  2. 使用命名构造函数,可以为一个类实现多个构造函数,能更清晰的表明你的意图。
  3. 构造函数不能继承,所以超类的命名构造函数也不会被继承。如果你希望子类也有超类一样的命名构造函数,你必须在子类中自己实现该构造函数。
class Point {
  num x;
  num y;
  //常规的构造函数
  Point(this.x, this.y);
    
  // 此处是命名构造函数
  Point.fromJson(Map json) {
    x = json['x'];
    y = json['y'];
  }
}

5、调用超类构造函数

  1. 默认情况,子类构造函数会自动调用超类的无名无参的默认构造函数。
  2. 超类的构造函数在子类构造函数体开始执行的位置调用。
  3. 如果提供了一个initializer list(初始化参数列表),则初始化参数列表在超类构造函数执行之前执行。
  4. 如果超类没有无名无参构造函数,则需要手工调用超类的其他构造函数。
  5. 调用超类构造函数时,在子类构造函数参数后使用冒号:super调用。
  6. 构造函数的执行顺序如下:
    • initializer list 初始化参数列表
    • superclass’s no-arg constructor 超类的无名构造函数
    • main class’s no-arg constructor 主类的无名构造函数
class Person {
  String firstName;

  Person.fromJson(Map data) {
    print('in Person');
  }
}

class Employee extends Person {
  // Person父类没有默认构造函数;
  // 这里必须调用父类的构造函数 super.fromJson(data).
  Employee.fromJson(Map data) : super.fromJson(data) {
    print('in Employee');
  }
}

main() {
  var emp = new Employee.fromJson({});
  // 此处打印结果:
  // in Person  	此处先执行父类的构造函数
  // in Employee    然后执行子类的构造函数
}

注:调用超类构造函数的参数无法访问 this。例如,参数可以为静态函数但是不能是实例函数。

initializer list 初始化列表

  1. 如果在构造函数的初始化列表中使用 super(),需要把它放到最后。
  2. 在构造函数体执行之前除了可以调用超类构造函数之外,还可以初始化实例参数。使用逗号分隔初始化表达式。
  3. 初始化列表非常适合用来设置 final 变量的值。
import 'dart:math';

class Point {
  final num x;
  final num y;
  final num distanceFromOrigin;
  // Initializer list sets instance variables before
  // the constructor body runs.
  Point(x, y)
      : x = x,
        y = y,
        distanceFromOrigin = sqrt(x * x + y * y);
}
main() {
  var p = new Point(2, 3);
  print(p.distanceFromOrigin);
}
//3.605551275463989

**警告:**初始化表达式等号右边的部分不能访问 this

6、重定向构造函数

  1. 有时候一个构造函数会调动类中的其他构造函数。此时就是重定向函数。
  2. 在构造函数声明后,使用冒号: this.xxx调用其他构造函数。
  3. 一个重定向构造函数是没有代码的,即不允许有代码块{}
class Point {
  num x;
  num y;

  // The main constructor for this class.
  Point(this.x, this.y);

  // Delegates to the main constructor.
  Point.alongXAxis(num x) : this(x, 0);
}

7、工厂方法构造函数

  1. 如果一个构造函数并不总是返回一个新的对象,则使用factory来定义这个构造函数。
  2. 实现单例可以使用工厂方法构造函数
class A{
  String name;
  //注意: 工厂构造函数无法访问 this。所以这儿要静态的
  static A _cache;

  //工厂构造函数的关键字为factory
  factory A([String name = 'A']){
    if(A._cache == null){
      A._cache=new A._newObject(name);
    }
    return A._cache;
  }
  //需要一个命名构造函数用来生产实例
  A._newObject(this.name);
}

void goFactory(){
  A a = new A('HelloWorld');
  print(a.name);

  A b = new A('HelloDart');
  print(b.name);

  print(a == b);
}
//打印结果
HelloWorld
HelloWorld
true

三、get和set 方法

  1. 在Dart语言中,每个实例变量都隐含的具有一个get方法,如果变量不是fianl的则还有个set方法。

  2. 在Dart中声明变量有两种方法,但调用方法一样。

    • 直接声明var temp ;,此时会有默认的getset方法与之对应;
     num a;
    
    • 使用getset方法声明。
      num get c {
       //自定义实现,不影响调用处
        return a + b; 
      }
      set c(num value) {
       //自定义实现,不影响调用处   
        a = value + b;
      }
    

** 注:**两种方式不可同时使用,否则会报:Error: 'c' is already declared in this scope.错误。

  1. 在使用过程中,当执行var temp = 实例.变量;这句时,就调用了变量的get方法;当执行实例.变量 = 5;时,就调用了变量的set方法。

  2. 基于第3点,此时修改变量的set和get方法的内部实现时,调用变量的代码不需要修改。

class GetAndSet {
  num a;
  num b;

  GetAndSet(this.a, this.b);
  //声明变量c 的get方法
  num get c {
    print('get c');
    return a + b;
  }
  //声明变量c的set方法
  set c(num value) {
    print('set c');
    a = value + b;
  }
}

main() {
  var i = new GetAndSet(4, 7); //此时 a = 4 ,b = 7;
  i.c = 4;//此时调用了set c  a = 4 + b = 11 ;b = 7 ,c = 18;
  var k = i.c;//此时调用了get c a = 11 ,b = 7 ,c = a + b = 18 ,k = c =18
  print('i.a = ${i.a},i.b = ${i.b},i.c = ${i.c},k = $k');
}
//打印结果
//set c
//get c
//get c  这里是print中i.c打印的
//i.a = 11,i.b = 7,i.c = 18,k = 18

四、抽象类

  1. 含有抽象方法的类就是抽象类,使用关键字abstract修饰类。
  2. 抽象方法是用;代替函数体也就是没有大括号{}run();
  3. 抽象方法只能在抽象类中出现,子类必须实现抽象方法。
  4. 抽象类中也可以有普通方法。
abstract class Car {
  run();
  space() {
    print('普通方法!');
  }
}

class Bus extends Car {
  int width = 4;

  Bus(this.width);
  //必须复写抽象方法,也可空实现
  @override
  run() {
    print('Bus 起飞跑');
  }
}

五、接口

  1. 在Dart中所有的类都是一个接口。类中隐式的定义了一个包含所有实例成员的接口。
  2. 在如果你想创建类A来支持类B的api,但不想继承B的实现,那么就实现B的接口。
  3. 一个类可以使用关键字implements实现一个或多个接口,并要实现接口中所有成员变量和函数
abstract class Car {
  run();

  space() {
    print('car的普通方法!');
  }
}

class Bus extends Car {
  int width = 4;

  Bus(this.width);

  //必须复写抽象方法,也可空实现
  @override
  run() {
    print('Bus 起飞跑');
  }
}

class C implements Bus {
  //成员变量也要实现
  @override
  int width;
  //从新实现 接口重写父类的抽象方法
  @override
  run() {
    print('C 的 run方法!');
  }
  //从新实现 接口继承父类的方法
  @override
  space() {
    print('C 的space方法!');
  }
}

main() {
  var c = new C();
  c.run();
  c.space();
}
//打印结果   接口实现的方法不会有父类的实现
C 的 run方法!
C 的space方法!

六、mixins机制

  1. mixin是一种在多类继承中,重用一个类代码的方法。

  2. 使用关键字with后面接一个或多个类。

  3. mixin类不可以含有构造函数

  4. mixin与接口不同都是,传统接口不包含实现部分,而mixin中是包含实现部分的。

  5. mixin与传统的继承和接口混合使用的区别:

    • 传统方式对于相同方法执行的优先级是:优先执行继承类,如果继承中没有,则执行实现类中的方法。
    class S {
       a() {
         print("S.a");
       }
       b() {
         print("S.b");
       }
       c() {
         print("S.c ");
       }
    }
    class A  {
      a() {
        print("A.a");
      }
      b() {
        print("A.b");
      }
    }
    class B {
      a() {
        print("B.a");
      }
        //如果不注释此方法,则打印结果都为 B.a   B.b    B.c
       //  b() {
       // 	 print("B.b");
       //  }
      c() {
        print("B.c ");
      }
    }
    class T extends B implements A,S{
      @override
      b() {
        print("T.b");
      }
    }
    main() {
      T t = new T();
      t.a();
      t.b();
      t.c();
      assert(t is S);//ture
    }
    //打印结果
    B.a
    T.b
    B.c
    
    • mixin机制的优先级是:with关键字后越靠后的优先级越高
    class S {
       a() {
         print("S.a");
       }
       b() {
         print("S.b");
       }
       c() {
         print("S.c ");
       }
    }
    class A  {
      a() {
        print("A.a");
      }
      b() {
        print("A.b");
      }
    }
    class B {
      a() {
        print("B.a");
      }
      b() {
        print("B.b");
      }
      c() {
        print("B.c ");
      }
    }
    class T extends B with A,S{
    }
    // class T = B with A, S; 可以写成这样   
    main() {
      T t = new T();
      t.a();
      t.b();
      t.c();
      assert(t is S);//ture
    }
    //打印结果
    S.a
    S.b
    S.c 
    //如果注释了S中的b方法,则打印结果为
    S.a
    A.b
    S.c
    

注意: 从 Dart 1.13 开始, 这两个限制在 Dart VM 上 没有那么严格了:

  • Mixins 可以继承其他类,不再限制为继承 Object
  • Mixins 可以调用 super()

总结:

mixin机制可以为类增加新的功能,使用with 将前后类中的相同方法通过优先级挑选出来与各自的不同方法,从新综合到一个新类中。如下:

  1. 定义各个mixin类
class S {
   a() {
     print("S.a");
   }
}

class A  {
  a() {
    print("A.a");
  }
  b() {
    print("A.b");
  }
}

class B {
  a() {
    print("B.a");
  }
  b() {
    print("B.b");
  }
  c() {
    print("B.c ");
  }
}
  1. 使用with
// class T = B with A, S;与下列方式一样
class T extends B with A,S{}

//此时的T类就相当于是
class T{
  a() {
     print("S.a");
  }
    
  b() {
    print("A.b");
  }
    
  c() {
    print("B.c ");
  }
}
  1. 使用
main() {
  T t = new T();
  t.a();
  t.b();
  t.c();
  assert(t is S);//ture
}
//打印结果
S.a
A.b
B.c 
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值