Dart入门(三)类、泛型、异常处理

Dart语言——45分钟快速入门(下)

Dart语法学习

Dart学习笔记(五):类

 

一、类

1、定义一个类

class Person {
  String name;
  int age;

  Person(String name, int age) {
    this.name = name;
    this.age = age;
  }
}

简化后可写成

class  Person {
    String name;
    int age;

    // 在构造方法中初始化成员变量时,可使用如下写法简化
    Person(this.name, this.age);
}

如需处理其他变量时,也可单独对其操作

class  Person {
    String name;
    int age;
// 如需处理其他变量时,也可单独对其操作 Person(this.name, this.age, String address){ print(address); } }

 

2、Getters 和 Setters方法

所有类中都包含隐式的getter方法,对于非final修饰的成员,类中还包含隐式的setter方法。

很多时候我们调用setter和getter方法并不仅仅是为了赋值和访问,而是为了一些额外的处理

class  Person {
    String userName;

    Person(this.userName);

    // 方法名前加get关键字
    String get name{
        return  "user:"  +  this.userName;
    }

    // 方法名前加set关键字
    set name(String name){
        // do something
        this.userName = name;
    }
}

可以通过实现 getters 和 setters 来创建附加属性,也就是直接使用 get 和 set 关键词:

class Rectangle {
   num left;
   num top;
   num width;
   num height;

   Rectangle(this.left, this.top, this.width, this.height);

   // 定义两个计算属性: 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;
}

main() {
   var rect = new Rectangle(3, 4, 20, 15);
   assert(rect.left == 3);
   rect.right = 12;
   assert(rect.left == -8);
}

 

3、命名构造方法

不支持构造方法重载,但有命名构造方法

class  Person {
    String userName;
    int age;

    Person(this.userName, this.age);

    // 命名构造方法
    Person.fromData(Map data) {
        this.userName = data['name'];
        this.age = data['age'];
    }
}

void  main() {
    // 使用命名构造方法创建对象
    var p = new Person.fromData({
        "name":"Bob",
        "age":19
    });
    print(p.userName);
}

Dart的构造函数

子类只能继承父类无名、无参数的构造函数(即子类不会继承父类的无名有参构造函数和命名构造函数)

默认情况下,子类只能调用父类的无名、无参数的构造函数,

如果父类不显示提供无名无参的构造函数,在子类中必须手动调用父类的一个构造函数(这里应该是这父类的一个有名构造函数)

lyh总结:子类构造函数需要手动调用父类的一个构造函数(若父类有一个显示的无名无参构造函数,子类可不用手动调父类的构造函数(默认调显示的无名无无参构造函数了)。) 

 

4、常量构造函数

如果想提供一个状态永远不变的对像,在Dart中,我们可以创建一个编译时常量对象,节省开销。

如果类提供的是状态不变的对象,那么可以把这些对象定义为编译时常量,实现这种功能可以定义const构造函数,且声明所有类的变量为final,如:

class ConstPoint {
  final num x;
  final num y;

  // 使用const修构造方法
  const ConstPoint(this.x, this.y);

  // 编译时常量对象,需使用const来创建对象

  // 常量如果是类级别的,请使用 static

  static final ConstPoint origin = const ConstPoint(0, 0);

}

void main() {
  print(ConstPoint.origin.x);
  print(ConstPoint.origin.y);
}

 

5、静态方法、静态常量

static 关键字来实现类级别变量和函数、类中的常量需要使用static const声明

静态成员不能访问非静态成员、非静态成员可以访问静态成员

class People{
  static int age;
  static void Say(){
    print('hello!');
  }
}

void main(){
  People.age = 10;
  People.Say();
}

 

6、工厂构造方法

当我们需要创建一个新的对象或者从缓存中取一个对象时,工厂构造方法就派上了用场。

注意:工厂构造函数不能用 this。

class  Logger {
    final String name;

    // 创建一个静态Map做为缓存,
    static final Map<String, Logger> _cache =  <String, Logger>{};

    // 定义一个命名构造方法,用下划线"_"修饰,将构造方法私有化
    Logger._internal(this.name);

    // 使用关键字factory修饰类同名构造方法
    factory Logger(String name) {
        if (_cache.containsKey(name)) {
            return _cache[name];
        } else {
            // 调用命名构造方法创建新对象
            final logger= new  Logger._internal(name);
            _cache[name] = logger; // 存入缓存
            return logger;
        }
    }
}

void  main() {
    var uiLog = new Logger('UI');
    var eventLog = new Logger('event');
}

 

7、构造方法重定向

有时候一个构造方法会调动类中的其他构造方法来实例化,这时候可以使用构造方法重定向,

class Point {
  num x;
  num y;

  // 同名构造方法
  Point(this.x, this.y);

  // 命名构造方法重定向到同名构造方法,中间使用一个冒号
  Point.alongXAxis(num x) : this(x, 0);
}

 

8、类的初始化列表

位于构造方法的小括号与大括号之间,在初始化列表之前需添加一个冒号

逗号分隔的一些赋值语句组成

适合用来初始化 final修饰的变量

调用是在构造方法之前

class  Point {
    final  num x;
    final  num y;
    final  num distance;

    Point(x, y)
        : x = x,
          y = y,
          distance =  sqrt(x * x + y * y){
             print("这是构造方法");
          }
}

void  main() {
    var p =  new  Point(2, 3);
    print(p.distance);
}

 

9、运算符重写

class Point {
  int x;
  int y;

  Point(this.x, this.y);

  // 使用operator关键字,为该类重载"+"运算符
  Point operator +(Point p) {
    return new Point(this.x + p.x, this.y + p.y);
  }

  // 为该类重载"-"运算符
  Point operator -(Point p) {
    return new Point(this.x - p.x, this.y - p.y);
  }
}

void main(){
   var p1 = new Point(1,5);
   var p2 = new Point(7,10);

   // 重载运算符后,类可以使用“+”、“-” 运算符操作
   var p3 = p1 + p2;
   var p4 = p2 - p1;

   print("${p3.x}, ${p3.y}");
   print("${p4.x}, ${p4.y}");
}

 

10、类的单继承

class Father {
    myFunction(){
        // do something
    }
}

class Son extends Father {

    @override
    myFunction(){
        super.myFunction();
        // do something
    }
}

 

11、mixin 混入

当我们的继承父类不是同一个的,同时子类里面需要实现同样的功能时,Mixin显得尤为重要。

理解Dart的Mixin继承机制

Dart | 什么是Mixin

demo1:

abstract class Animal {}

abstract class Mammal extends Animal {}

abstract class Bird extends Animal {}

abstract class Fish extends Animal {}

abstract class Walker {
  // This class is intended to be used as a mixin, and should not be
  // extended directly.
  factory Walker._() => null;

  void walk() {
    print("I'm walking");
  }
}

abstract class Swimmer {
  // This class is intended to be used as a mixin, and should not be
  // extended directly.
  factory Swimmer._() => null;

  void swim() {
    print("I'm swimming");
  }
}

abstract class Flyer {
  // This class is intended to be used as a mixin, and should not be
  // extended directly.
  factory Flyer._() => null;

  void fly() {
    print("I'm flying");
  }
}

class Dolphin extends Mammal with Swimmer {}

class Bat extends Mammal with Walker, Flyer {}

class Cat extends Mammal with Walker {}

class Dove extends Bird with Walker, Flyer {}

class Duck extends Bird with Walker, Swimmer, Flyer {}

class Shark extends Fish with Swimmer {}

class FlyingFish extends Fish with Swimmer, Flyer {}

demo2:

class Father1 {
    a(){
      print("this is a func");
    }

    common(){
        print("common Father1");
    }
}

class Father2 {
    b(){
      print("this is b func");
    }

    common(){
        print("common Father2");
    }
}

class Father3 {
    c(){
      print("this is c func");
    }

    common(){
        print("common Father3");
    }
}

//定义子类
class Son extends Father1 with Father2,Father3{

}

void main() {
  var obj = new Son();
  obj.common();
  obj.a();
  obj.b();
  obj.c();
}

打印结果

common Father3
this is a func
this is b func
this is c func

以上写法中,也可以直接使用with,等价于如下写法

class Son with Father1,Father2,Father3{

}

 

ps:

1、另外还需要注意一点,Dart中没有构造方法的重载,不能写两个同名的构造方法。

2、子类会继承父类显示的的无名无参的构造函数,父类中有命名构造函数,子类要手动继承。

3、@override (重写)

父类中有方法func(),子类会继承,子类想重写func(),会自动加上@override标记,且在方法中会自动加上super.func(); 

如果删掉super.func(),则父亲类中的func()不会被调用,

另外,子类中没实现func(),则子类调用func(),父类func()会被调用。

4、默认情况下,子类只能调用父类的无名,无参数的构造函数; 父类的无名构造函数会在子类的构造函数前调用; 如果initializer list 也同时定义了,则会先执行initializer list 中的内容,然后在执行父类的无名无参数构造函数,最后调用子类自己的无名无参数构造函数。即下面的顺序:
  • initializer list(初始化列表)
  • super class’s no-arg constructor(父类无参数构造函数)
  • main class’s no-arg constructor (主类无参数构造函数)

对于final变量的值,可以在初始化列表中指定

class Person {
   String firstName;

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

class Employee extends Person {
   // 父类没有无参数的非命名构造函数,必须手动调用一个构造函数     
   Employee.fromJson(Map data) : super.fromJson(data) {
      print('in Employee');
   }
}

main() {
   var emp = new Employee.fromJson({});

   // Prints:
   // in Person
   // in Employee
   if (emp is Person) {
     // Type check
     emp.firstName = 'Bob';
   }
   (emp as Person).firstName = 'Bob';
}

 

二、抽象类 

Dart语言没有提供interface关键字来定义接口,

但是Dart语言中保留了抽象类,同Java,使用abstract关键字来修饰抽象类。而Dart中的抽象类,实际上就相当于Java中的接口。

抽象类是不能被实例化的,子类继承抽象类时,必须实现全部抽象方法。

如果你想让你的抽象类被实例化,请定义一个 工厂构造函数 。

abstract class Base {
    // 省略函数体即可定义抽象方法,不需加关键字
    func1();
    func2();
}

 

三、隐式接口

实际上在Dart中,每个类都隐式的定义了一个包含所有实例成员的接口, 并且该类实现了这个接口

 因此,如果我们想实现某个接口,但有又不想继承,则可以使用这种隐式接口机制。我们需要用到关键字implements

class People {
  void greet(){
    print("Hello");
  }
}

class Student implements People{
  @override
  void greet(){
    print("Hi,I'm Alice.");
  }
}

greet(People p){
  p.greet();
}

void main() {
  greet(new Student());
}

接口是可以多实现的,如下:

class TonyStart implements American, Scientist, Richman, Playboy {
    // ...
}

 

四、泛型

var names = new List<String>();
names.add("zhangsan")

var maps = new Map<int, String>();
maps[1]="value";

字面量写法

var infos = <String>['Seth', 'Kathy', 'Lars'];

var pages = <String, String>{
  'index.html': 'Homepage',
  'robots.txt': 'Hints for web robots'
};

 

五、异常处理

  try {
      // 使除数为0
      print(11~/0);
  } on IntegerDivisionByZeroException {
      print("除数为0");
  }on Exception{
      print("Exception");
  }finally {
      print("finally");
  }

不关心具体异常,只想捕获,避免异常继续传递

  try {
      print(11~/0);
  } catch(e){
      // 打印报错信息
      print(e);
  }finally {
      print("finally");
  }

如果想获取更多异常信息,可以使用两个参数的catch,第二个参数是异常的调用栈信息

  try {
      print(11~/0);
  } catch(e,s){
      print(s);
  }

如果你既想针对不同异常进行不同处理,还想打印调用栈信息,那就将两种结合起来使用

  try {
      print(11~/0);
  } on IntegerDivisionByZeroException catch(e,s){
      print(s);
  } on Exception catch(e,s){
      print(s);
  }

 


 

 

 

 

 

posted @ 2019-11-22 15:19  素染年华  阅读(620)  评论(0编辑  收藏  举报