Dart入门宝典第四章——面向对象编程之继承、抽象类、接口、Mixins的介绍

393 阅读5分钟

继承

  • 使用关键字extends继承一个类
  • 子类会继承父类可见的属性和方法,不会继承构造方法
  • 子类能够复写父类的方法、getter和setter
  • 单继承,多态性

我们定义一个动物类Animal类,具有名字、年龄,还有一个是否食肉的属性,默认为true,还有一个run()方法。

class Animal{
  String name;
  int age;

  bool get eatMeat => true;

  void run(){
     print("run...");
  }
}

定义一个Sheep类集成Animal类,重写食肉属性为false,重写run()方法,另外它还有一个自己的特有方法shout()

class Sheep extends Animal{

  //重写父类的属性
  @override
  bool get eatMeat => false;

  //重写父类的方法
  @override
  void run() {
     print("Sheep run...");
  }

  void shout(){
    print("mie mie mie...");
  }
}

创建Sheep类并调用其run()方法,由于shout()方法是Sheep类所特有的方法,所以需要先将Animal类型的sheep变量进行转换,可以使用 as 转换,或使用以下的判断:

void main() {
  Animal sheep = new Sheep();//使用Animal类接收
  sheep.run();//Animal类具有run()方法
  if(sheep is Sheep){
     //shout()方法为子类Sheep特有,需要转换为子类Sheep才能调用
     sheep.shout();
  }
}

打印结果为:

Sheep run...
mie mie mie...

继承中的构造方法

  • 子类的构造方法默认会调用父类的无名无参构造方法
  • 如果父类没有无名无参构造方法,则需要显示调用父类构造方法
  • 在构造方法参数后使用:显示调用父类的构造方法

定义一个Person类,包含一个无名构造方法和命名构造方法withAge()

class Person {
  String name;
  int age;

  Person(this.name);

  Person.withAge(this.age);
}

定义一个Student类继承Person类,重写其两个构造方法:

class Student extends Person {
  Student(String name) : super(name);

  Student.withAge(int age) : super.withAge(age);
}

通过两个不同的构造方法构造student实例:

void main() {
   var student1 = new Student("chaychan");
   var student2 = new Student.withAge(26);
   print(student1.name);
   print(student2.age);
}

构造方法执行顺序

  • 父类的构造方法在子类构造方法体开始执行的位置调用
  • 如果有初始化列表,初始化列表会在父类构造方法之前执行
class Student extends Person {
  final String gender;

  Student(String name) : gender = "Male", super(name);
}

抽象类

  • 抽象类用abstract表示,不能直接被实例化
  • 抽象方法不需要用abstract修饰,无实现
  • 抽象类可以没抽象方法
  • 有抽象方法的类一定得声明为抽象类
abstract class Animal {
   void run();
}

class Dog extends Animal {
  @override
  void run() {
     print("run...");
  }
}

接口

  • 类和接口是统一的,类就是接口
  • 每个类都隐式的定义了一个包含所有实例成员的接口
class Animal {
  String name;
  int age;

  void run() {
    print("run...");
  }
}

class Dog implements Animal {
  @override
  int age;

  @override
  String name;

  @override
  void run() {
    print("run...");
  }
}

可以看到此时Animal类即是接口,Dog类实现这个接口需要复写Animal类的所有属性和方法。

  • 如果是复用已有类的实现,使用继承方式(extends)

上述相当于复用已有类的实现,可以使用继承的方法:

class Dog extends Animal {
}
  • 如果只是使用已有类的外在行为,使用实现方式(implements)
abstract class Animal {
   void run();
}

class Dog implements Animal{
  @override
  void run() {
     print("run...");
  }
}

Mixins

  • Mixins类似于多继承,是在多类继承中重用一个类代码的方式
  • 使用关键字with连接一个或多个mixin
void main() {
   var d = D();
   d.a();
   d.b();
   d.c();
}

class A{
   void a(){
     print("a");
   }
}

class B{
  void b(){
    print("b");
  }
}

class C{
  void c(){
    print("c");
  }
}

class D extends A with B, C{

}

可以看到D类通过Mixins继承了A、B、C三个类,同时具有a()、b()、c()方法,使用Mixins的方法需要通过extends一个类和with其他类,不能直接使用with A, B, C。

  • 作为Mixins的类不能有显示声明构造方法
class D extends A with B, C{

}

上述D类中B、C是作为Mixins的类,他们不能显示声明构造方法,即只能使用默认的方法,否则编辑器会提示报错。

  • 作为Mixins的类只能继承自Object
class C2{
  
}

class C extends C2{
  void c(){
    print("c");
  }
}

class D extends A with B, C{ //此时C下面会报红

}

操作符覆写

  • 覆写操作符需要在类中定义
 返回类型 operator 操作符 (参数1,参数2,...){
     实现体
     return 返回值
 }
  • 如果覆写==,还需要覆写对象的hashCode getter方法

  • 可以覆写的操作符有:

  < > <= >= 
  
  + - * / ~/ %
  
  | & ^ << >>
  
  [] []= ~ ==
  

演示覆写>和[]操作符,比较两个人的大小,通过比较年龄:

void main() {
  var p1 = Person(18);
  var p2 = Person(20);
  print(p1 > p2);
  print(p1["aage"]);
}

class Person {
  int age;

  Person(this.age);

  bool operator >(Person person) {
    return this.age > person.age;
  }

  int operator [](String str) {
    if ("age" == str) {
      return age;
    }
    return 0;
  }
}

打印结果为:

false
0

枚举

  • 枚举是一种有穷序列集的数据类型
  • 使用关键字enum定义一个枚举
  • 常用于代替常量,控制语句
enum Season{
  spring,
  summer,
  autumn,
  winter
}

Dart枚举的特性:

  • index从0开始,依次累加
  • 不能指定原始值
  • 不能添加方法
void main() {
   print(Season.spring.index);
   print(Season.summer.index);
   print(Season.autumn.index);
   print(Season.winter.index);
}

打印结果为:

0
1
2
3

和Java中的枚举有所不同,Dart中的枚举不能指定原始值和添加方法。

泛型

  • Dart中类型是可选的,可使用泛型限定类型
void main() {
  var list1 = new List();
  list1.add(1);
  list1.add("2");
  
  var list2 = new List<int>();
  list2.add(1);
  list2.add(2);
}

上述代码中,list1没有指定泛型,可以添加任意类型的值,而list2指定了泛型为int,只能添加整数型的值。

  • 使用泛型能够有效的减少代码重复(工具方法可以通过传入指定泛型返回对应类型的结果,无需因为需要返回不同类型的结果编写重复代码)

泛型的的使用

  • 类的泛型
class DataHelper<T>{
   T data;
   setData(T data){
      this.data = data;
   }
}

void main() {
   var helper1 = DataHelper<int>();
   helper1.setData(1);

   var helper2 = DataHelper<String>();
   helper2.setData("2");
}

可以看到我们定义了一个DataHelper类,通过传入类的泛型限制传入的data类型。

  • 方法的泛型
void main() {
   var helper = DataHelper();
   helper.checkData<int>(1);
   helper.checkData<String>("Hello");
}

class DataHelper{
   checkData<T>(T data){
      if(data is int){
        print("data is int");
      }else if(data is String){
        print("data is String");
      }
   }
}

可以看到我们定义了一个需要带有泛型的方法checkData,通过指定泛型限制参数的类型。

好了,关于Dart的入门相关知识点已经介绍完毕,可以尝试去开发和实践了。