Dart基础语法总结

1,079 阅读17分钟

Dart介绍

  1. Dart与Flutter的关系就像TypeScript与Angular的关系
  2. Dart万物皆对象,数字也是,函数也是,null也是,所有对象都继承自Object
  3. Dart在running走之前解析所有代码,指定类型和编译时常量,能提升代码运行速度
  4. Dart没有public, protected,private关键字,以下划线(_)定义的变量就是私有变量

Dart关键字

----
abstract 抽象类doimport 导入库super 超类
as 类型转换dynamic 动态类型inswitch 循环
assert 断言elseinterface 接口(貌似已经移除)sync* & yield 同步生成器
enum 枚举implements 实现接口is 类似instanceofthis
async* 标识异步生成器函数export 导出library 定义库的名字throw 抛出异常
await 等待external 代码的实现由外部提供mixin 混入(多继承)true & false
breakextendsnewtry & catch
casefactory 标识工厂构造函数nulltypedef 声明函数类型
operator 向量类varclassfinal
part 将库拆分为多个Dart文件voidconstfinally
rethrow 让异常可传播whilecontinuefor
returnwith 配合mixincovariant 防止参数缩窄变化报错get & set
defaultifstaticdeferred 声明一个延迟加载库

Dart内置支持的类型与常用api

  • numbers(拥有int, double两个子类,都继承自numbers)
  • strings
  • booleans
  • lists
  • maps
  • runes(用于在字符串中表示 Unicode 字符)
  • symbols

定义常量

  1. const
    const 是在编译时期就必须赋值,而且 const 在类中的话,就必须定义为 static const.(const变量是隐式的final)
  2. final
    被final修饰的变量其实不算常量,就是不可变的变量,final只能在类初始化时赋值一次。编译时看不到值

num类型常用API

官网地址: api.dartlang.org/stable/2.5.…

  1. isEven 这是表示这个数是不是偶数
void main(){
  int x = 18;
  print(x.isEven);
}
  1. isOdd 判断是否是奇数

void main(){
  int x = 19;
  print(x.isOdd);
}
  1. toDouble 这是int转double

void main(){
  int x = 3;
  print(x.toDouble()); //返回结果是3.0
}
  1. ...各种数学方法(round,floor,ceil,truncate,abs,isInfinite等等)

String 常用API

官网地址: api.dartlang.org/stable/2.5.…

  1. 单行与多行字符串
//官网例子
'Single quotes';
"Double quotes";
'Double quotes in "single" quotes';
"Single quotes in 'double' quotes";
// 三个单引号
'''A
multiline
string''';
// 三个双引号
"""
Another
multiline
string""";
  1. 使用 + 符号连接字符串
'Dart ' + 'is ' + 'fun!'; // 'Dart is fun!'
  1. 相邻的字符串文字进行串联:
'Dart ' 'is ' 'fun!';    // 'Dart is fun!'
  1. 字符串插值
String str = 'yuanchen';
String str2 = 'username is ${str}'; //username is yuanchen
  1. 通过索引符号访问代码单元的字符串表示形式:
String str = 'yuanchen';
print(str[0]) // 'y'
  1. isEmpty 如果此字符串为空,则返回true。
  2. isNotEmpty 如果此字符串不为空,则返回true。
  3. length 返回字符串的长度
  4. compareTo 粗暴理解为判断当前数字大于等于还是小于指定数字
/*
* 两个数字相等返回0
* 当前数字大于指定数字返回 1
* 当前数字小于指定数字返回 -1
*/
double a = 2.4;
print(a.compareTo(12)) // 1
print(a.compareTo(2.4)) // 0
print(a.compareTo(0)) // -1
  1. contains 是否包含指定字符串
print('yuanchen'.contains('yuan')); // true
  1. indexOf 同JavaScript indexOf
print('yuanchen'.indexOf('u')); // 1
  1. lastIndexOf 字符串最后一次出现的位置
print('www.baidu.com'.lastIndexOf('.')) // 9
  1. replaceAll 替换所有匹配的子串
const String str = "yuanyuanchen";
 print("New String: ${str.replaceAll('yuanyuan','yuan')}"); //yuanchen
  1. split 字符串转数组
  2. splitMapJoin 拆分字符串,转换其部分,然后将它们组合为新的字符串
const String str = 'a b,c';
// 查询" , ",用onMatch的返回值替换" , " 用onNonMatch的返回值替换其他
String str1 = str.splitMapJoin(",",  
  onMatch: (Match match) => "a",
  onNonMatch: (String nonMatch) => "b"
);

print(str1) // a b,c -> bab
  1. toLowerCase 字符串转换为小写
  2. toUpperCase 字符串转换为大写
  3. startswith 方法用于检查字符串是否是以指定子字符串开头, 如果参数有 beg 和 end 指定值,则在指定范围内检查。
str = "this is string example....wow!!!";
print str.startswith( 'this' ); // true
print str.startswith( 'is', 2, 4 ); // true
print str.startswith( 'this', 2, 4 ); // false
  1. trim去除前后空格 trimLeft & trimRight 同语义

  2. noSuchMethod 使用不存在的方法时调用

List 常用API

官网地址: api.dartlang.org/stable/2.5.… 先准备一个List,后面全部操作这个List

const List<Map<String,dynamic>> students = [
  { 'name': 'tom', 'age': 16 },
  { 'name': 'jack', 'age': 18 },
  { 'name': 'lucy', 'age': 20 }
];
List numbers = [2, 8, 5, 1, 7, 3];
  1. forEach 遍历一个list或者map
students.forEach((student) => print(student));
// {name: tom, age: 16}
// {name: jack, age: 18}
// {name: lucy, age: 20}
Map tom = { 'name': 'tom', 'age': 16 };
tom.forEach((key, value) => print(key + ' - ' + value.toString()));
// name - tom
// age - 16
  1. map可以用来操作已知数组里的每一项,然后返回一个新数组
var messages = students.map((student) => 'Hello ' + student['name']).toList();
print(messages);
// [Hello tom, Hello jack, Hello lucy]
  1. contains() 用于判断数组是否包含某个元素
print(numbers.contains(5));
// true
  1. sort() 用于排序
numbers.sort((num1, num2) => num1 - num2);
print(numbers);
// [1, 2, 3, 5, 7, 8]
  1. reduce(), fold()
  • reduce() 将数组中的每一个值与前面返回的值相加,最后返回相加的总和
  • fold() 用法跟 reduce() 基本一样,只不过是可以提供一个初始值
  • 还是JavaScript的reduce强大,hahahhaha~
var sum = numbers.reduce((curr, next) => curr + next);
print(sum);
// 26

var sum2 = numbers.fold(10, (curr, next) => curr + next);
print(sum2);
// 36
  1. every() 用于判断数组中的每一项是否均达到了某个条件
var isAgeOver20 = students.every((student) => student["age"] > 20);
print(isAgeOver20);
// false

var isAgeOver15 = students.every((student) => student["age"] > 15);
print(isAgeOver15);
// true
  1. where(), firstWhere(), singleWhere()
  • where() 返回数组中满足给定条件的元素集合
  • firstWhere() 返回数组中满足给定条件的第一个元素
  • singleWhere() 返回数组中满足给定条件的唯一一个元素,如果有多个元素满足条件会抛出异常
var ageOver16 = students.where((student) => student["age"] > 16);
print(ageOver16.toList());
// [{name: jack, age: 18}, {name: lucy, age: 20}]

var ageFirstOver16 = students.firstWhere((student) => student["age"] > 16, orElse: () => null);
print(ageFirstOver16);
// {name: jack, age: 18}

var ageUnder20 = students.singleWhere((student) => student["age"] < 16, orElse: () => null);
print(ageUnder20);
// null
  1. take(), skip()
  • take(n) 从数组里取 n 个元素
  • skip(n) 跳过数组中的 n 个元素
List arr = [1, 3, 5, 2, 7, 9];

print(arr.take(3).toList());
// [1, 3, 5]
print(arr.skip(4).toList());
// [7, 9]
print(arr.take(3).skip(2).take(1).toList());
// [5]
  1. List.from() 克隆一个数组
List arr = [1, 3, 5, 2, 7, 9];
var clonedArr = List.from(arr);
print(clonedArr);
// [1, 3, 5, 2, 7, 9]
  1. expand() 扩大展开列表(类似js中的flat)
var arr1 = [[2, 5], [7], [11, 12]];
var flattened = arr1.expand((item) => item).toList();
print(flattened);
// [2, 5, 7, 11, 12]

var arr2 = [2, 5, 8];
var computed = arr2.expand((item) => [item * 8]).toList();
print(computed);
// [16, 40, 64]

// 当对每一项进行计算时类似于 map()
var computed2 = arr2.map((item) => item * 8).toList();
print(computed2);
// [16, 40, 64]
  1. add(), addAll()
  • add() 向数组中添加一个元素
  • addAll() 向数组中添加另一个数组的所有元素
var arr1 = [1, 3, 5, 9, 2, 1];
arr1.add(10);
print(arr1);
// [1, 3, 5, 9, 2, 1, 10]
arr1.addAll([15, 21]);
print(arr1);
// [1, 3, 5, 9, 2, 1, 10, 15, 21]
  1. clear() 清空数组
numbers.clear(); //[]

Map 常用API

官网地址: api.dartlang.org/stable/2.5.…

先准备一个map 后面操作它

const Map<dynamic, String>map = {'first': 'dart', 2: 'JavaScript'};
  1. map赋值
Map<String,dynamic> letterMap = new Map();
letterMap["a"] = "A";
letterMap["b"] = "B";
letterMap["c"] = "C";

print(letterMap); //{a:A, b: B, c: C}
  1. .isEmpty() 是否是空, .isNotEmpty() 是否非空
print(map.isEmpty()); //false
print(map.isNotEmpty()); //true
  1. .keys、.values 获取map的键或者值
print(map.keys); //(first,2)
print(map.values); //(dart,JavaScript)
  1. containsKey()、containsValue() 判断包含某个key、value
print(map.containsKey('first')); //true
print(map.containsValue('PHP')); //false    
  1. remove(),removeWhere() 指定键名删除与根据条件删除
map.remove('first');
print(map); // {2:'JavaScript'}
 map.removeWhere((key, value) => key is String);
 print(map); // {2:'JavaScript'}
  1. 遍历map的几种方式
//遍历所有的 key
for (String key in map.keys) {
  print('$key');
}

//遍历所有的 value
for (String value in map.values) {
  print('$value');
}

//遍历所有的 key-value
map.forEach((key, value) => print('$key == $value'));
  1. addAll 添加其他键值对到map中
map.addAll({3:"PHP", 4: "JAVA" });
  1. update(), updateAll() update
  • 如果在map中找到了指定的key,就会根据第二个参数指定的规则去修改key对应的value的数据
  • 如果没有找到指定的key,就会去查看第三个参数是否设置
  • 如果设置了第三个参数,执行完之后会在当前map中添加元素
//存在需要更改的key时
  var maps = {'one':'10', 'tow':20,'three': 30};
  maps.update('one',(value) {
    return '999';
  });
  print(maps);// {one: 999, tow: 20, three: 30}

//不存在需要更改的key 但是指定了第三个参数
  maps.update('four',(value) {
    return '999';
  },ifAbsent: () => '123');
   print(maps);// {one: 10, tow: 20, three: 30, four: 123}

updateAll

  var maps = {'one':10, 'tow':20,'three': 30};
  maps.updateAll((key, value) {
    return value+1;
  });
  print(maps); //{one: 11, tow: 21, three: 31}

Set常用方法

官网地址: api.dartlang.org/stable/2.5.…

  1. set不支持字面量方式常见,只能用过Set构造函数创建,并且不能有重复的值出现
var Set set = new Set();
set.add('a');
set.addAll(['b','c']);
print(set); // {a,b,c}
  1. difference(), intersection(), union()
  • difference 求两个set集合的差集
  • intersection 求两个set集合的交集
  • union 合并set并去重
//差集
const List<int> list1 = [1,2,3,4];
const List<int> list2 = [2,3];
Set set1 = list1.toSet();
Set set2 = list2.toSet();
print(set1.difference(set2));// {1,4}
//交集
const List<int> list1 = [1,2,3,4];
const List<int> list2 = [2,3];
Set set1 = list1.toSet();
Set set2 = list2.toSet();
print(set1.intersection(set2));// {1,4}
//合并去重
const List<int> list1 = [1,2,3,4];
const List<int> list23 = [2,3,5];
Set set1 = list1.toSet();
Set set2 = list23.toSet();
print(set1.union(set2)); // {1, 2, 3, 4, 5}
  1. removeAll(), removeWhere() 按值删除和按条件删除
  • removeAll 按值删除
  • removeWhere 按条件删除
const List<int> list1 = [1,2,3,4];
Set set1 = list1.toSet();
set1.removeAll( { 2, 3 } );
print(set1); //{1, 4}
Set set2 = list1.toSet();
set2.removeWhere((value) => value > 1);
print(set2); //{1}

Dart类 Class

类与对象

1. 什么是Dart中的类

  • 使用关键字class声明一个类
  • 使用关键字new创建一个对象,new可省略(推荐)
  • 所有对象都继承自Object类
void main() {
  Persion person = new Persion();
  Persion person1 = Persion(); //可以不使用new关键字
}

class Person {
}

2. 类中的属性和方法

  • 属性会默认生成getter和setter方法
  • 使用final声明的属性只能有getter方法
  • 属性和方法只能通过.访问
  • 方法不能重载
void main() {
  Person person = Person();
  person.name = "yuanchen";
  person.age = 18;
  //访问属性
  print(person.name);
  //访问方法
  person.work();
}

class Person {
  String name;
  int age;
  final  String address = ''; //实例只能访问不能更改
  void work() {
    print("Name is $name, Age is $age, He is Working");
  } 
}

类和成员的可见性

  1. Dart中的可见性以library(库)为单位
  2. 默认情况下。每一个Dart文件就是一个库
  3. 使用下划线(_)表示私有性
  4. 使用import导入库
//a.dart
import 'b.dart';
void main() {
  Person person = Person();
   person.name = "yuanchen";
   person.age = 18;
   print(person.name);
}
//b.dart
class Person{ //如果Person加上下划线(_) 在a.dart将无法访问到Person类,只在当前文件(b.dart)生效
  String name;
  int age;
  void work() {
    print("Name is $name, Age is $age, He is Working");
  } 
}

计算属性

  1. 计算属性的值通过计算而来,本身不存储值
  2. 计算属性赋值,通过计算转换到其他实例变量
  3. 计算属性就是当前类的属性,而方法主要是为了表达类的某一种行为
void main() {
  Rectangle rectangle = Rectangle();
  rectangle.width = 20;
  rectangle.height = 10;
  print(rectangle.area);
  //使用setter
  rectangle.area = 200;
  print(rectangle.width);
}

class Rectangle {
  num width, height;
  //通过getter返回面积
  num get area {
    return width * height;
  }
  //通过setter设置宽度
  set area(value) {
    width = value / 20;
  }
}

构造方法

  1. 如果没有显式定义构造方法,会有一个默认的构造方法
  2. 如果存在自定义的构造方法, 默认的构造方法无效
void main() {
  Person person = Person('yuanchen', 20);
  print('${person.name}-- ${person.age}');
}

class Person {
  String name;
  int age;
  final  String gender = '';

  //如果不写 Dart会默认创建这样一个构造方法
//  Person() {}

  Person(String name, int age) {
    this.name = name;
    this.age = age;
  }
  void work() {
    print("Working");
  }
}
  1. 构造函数语法糖
void main() {
  Person person = Person('yuanchen', 20);
  print('${person.name}-- ${person.age}');
}

class Person {
  String name;
  int age;
  //构造函数的语法糖
  Person(this.name, this.age);
  //等价于
//  Person(String name, int age) {
//    this.name = name;
//    this.age = Element.age;
//  }
}
  1. 通过final定义的变量只能通过语法糖或者初始化列表(后面讲)的形式初始化,因为这两种形式初始化属性都是在构造方法执行前初始化的属性
void main() {
  Person person = Person('yuanchen', 20,'男');
  print('${person.name}-- ${person.age}');
}

class Person {
  String name;
  int age;
  final String gender; //final定义的变量只能通过语法糖或者初始化列表的形式初始化
  //构造函数的语法糖
  Person(this.name, this.age,this.gender);
}
  1. 构造方法不能重载,但是有命名构造方法
//使用命名构造方法,可以实现多个构造方法
// 使用类名.方法的形式实现
void main() {
  Person person = Person('yuanchen', 20,'男');
  print('${person.name}-- ${person.age}');
  //实例化命名构造方法
  Person person1 = Person.withName('男');
  print(person1.gender);
}

class Person {
  String name;
  int age;
  final String gender;
  //构造函数的语法糖
  Person(this.name, this.age,this.gender);
  //命名构造方法
  Person.withName(this.gender);
}
  1. 构造方法可选参数
void main() {
  //这里实例化的时候可以不初始化age属性 运行时会使用给定的默认值
  Person person = Person('yuanchen');
  print('${person.name}-- ${person.age}');
  //如果传递了 会覆盖给定的默认值
  Person person1 = Person('yuanchen',age: 21);
  print('${person1.name}-- ${person1.age}'); //打印出 yuanchen-- 21
}

class Person {
  String name;
  int age;
  //构造函数的语法糖
  Person(this.name, {this.age = 20});
}

常量构造方法

  1. 如果类是不可变状态,可以把对象定义为编译时常量
  2. 使用const声明构造方法,当前类的所有属性必须都为final
  3. 使用const声明对象,可以省略
  4. 使用场景比如属性只会被赋值一次的话,可以使用常量构造方法,因为常量在运行时更快,在编译期就已经确定了
void main() {
  const person = const Person('yuanchen', 20);
  print(person.name);
}

class Person {
  final String name;
  final int age;
  const Person(this.name, this.age);
}

工厂构造方法

  1. 构造方法前加factory实现一个工厂方法
  2. 在工厂构造方法里面可以返回对象
  3. 使用场景就是flutter中的Json手动序列化
//官网例子 实现缓存效果
class Logger {
  final String name;
  static final Map<String, Logger> _catch = <String, Logger> {};
  factory Logger(String name) {
    if(_catch.containsKey(name)){
      //工厂构造方法可以返回对象
      return _catch[name];
    }else {
      final logger = Logger._internal(name);
      _catch[name] = logger;
      //工厂构造方法可以返回对象
      return logger;
    }
  }
  Logger._internal(this.name);
  void log(String msg) {
    print(msg);
  }
}

初始化列表

1.初始化列表会在构造方法之前执行
2. 使用逗号分隔初始化列表
3. 初始化列表通常用于设置final变量的值

void main() {
  Person person =  Person({'name':'yuanchen','age': 20, 'gender':'男'});
  print(person.gender);
}

class Person {
   String name;
   int age;
   final String gender;
   //初始化列表 也可以对非final属性赋值
   Person(Map map): gender = map['gender'], name = map['name']{
     this.age = map['age'];
   }
}

静态成员

  1. 使用static关键字声明类级别的属性或者函数
  2. 静态成员不能访问非静态成员, 非静态成员可以访问静态成员
  3. 类中的常量需要使用static const类声明
void main() {
  Page page = Page();
  //静态方法属于当前类,不属于实例,只能通过当前类来调用
  Page.scrollDown();
}
class Page {
  //类中声明常量要使用static
  static const int maxPage = 10;
  static int currentPage = 1;
  static void scrollDown() {
    currentPage = 1;
    print('scrollDown....');
  }

  void scrollUp() {
    //scrollUp()方法为非静态成员 所以非静态成员可以访问静态成员
    currentPage++;
    print('scrollUp....');
  }
}

对象操作符

  1. 级联操作符 (..)
  2. as 操作符强制更改变量类型
void main() {
  Person person =  Person();
  //级联操作符
  person..name = 'yuanchen'
        ..age = 20;

  // as 操作符
  var person1;
  //这里虽然person1没有被赋值,但是通过as强制转换为了Person类型就可以调用类中的属性或者方法
  // 但是当前情况会报错 因为虽然为Person类型 但是它的值为null 使用name属性会报错
  (person as Person).name = 'zs';
}

class Person {
   String name;
   int age;
}

对象call方法

  1. 如果类实现了call()方法,那么当前类的对象可以作为方法使用(不建议使用)
void main() {
  Person person =  Person('yuanchen',20);
  //直接把对象当作方法来用
  person('zs',30);
}

class Person {
   String name;
   int age;
   Person(this.name,this.age);
   //实现call方法 名字必须是call
   Map call(String name, int age) {
      print('Name is $name, Age is $age, He is working');
      //有返回值
      return {'name':name,'age':age};
   }
}

继承

  1. 使用关键字extends 继承一个类
  2. 子类会继承父类可见的属性和方法,不会继承构造方法
  3. 子类能够复写父类的方法,getter setter
  4. 单继承,多态性
  5. 所有类默认都继承自Object 因为默认有toString方法
// Person.dart 
class  Person {
  String name;
  int age;
  String _birthday;
  bool get isAdult => age > 18;
  void run() {
    print('Person is running...');
  }
}

//Student.dart
import 'Person.dart';
void main() {
  Student student = new Student();
  student.study(); //Student study....
  student.name = 'yuanchen';
  student.age = 16;
  //student._birthday = '4.29'; 不能使用父类的私有属性
  student.run(); // 重写了父类的方法 打印出Student is running ...
  print(student.isAdult); //重写了父类的计算属性 打印出true

  //多态性 
  // 这里将子类的实现赋值给父类的引用 所以这里person这个实例只能调用person的方法和属性
  Person person = new Student();
  person.age = 16;
  print(person.isAdult);// true 因为这里虽然是父类的类型 但是用的是子类的实现 所以返回true
}

//通过extends 继承Person
class Student extends Person{
  void study() {
    print('Student study....');
  }
  //重写父类的计算属性
  bool get isAdult => age > 15;

  //重写父类的方法
  void run() {
    print('Student is running ... ');
  }
}

继承中的构造方法

  1. 子类的构造方法默认会调用父类无名无参的构造方法
void main() {
  Student student = Student(); //会打印出 Person...
}

class Person {
  String name;
  Person() {
    print('Person....');
  }
}
class Student extends Person {
  int age;
}
  1. 如果父类没有无名无参的构造方法,需要显式调用父类构造方法
  2. 在构造方法参数后使用冒号(:) 显示调用父类构造方法
void main() {
  Student student = Student('yuanchen');
  print(student.name);
}

class Person {
  String name;
  Person(this.name);
  Person.withName(this.name);
}
class Student extends Person {
  int age;
  //如果父类中没有无名无参的构造方法 就需要显示的调用构造方法
    Student(String name) : super(name);
  //如果父类中有命名构造方法 也可以显式调用父类命名构造方法
  //Student(String name): super.withName(name);
}
  1. 父类的构造方法在子类构造方法体开始执行之前调用
  2. 如果有初始化列表,初始化列表会在父类构造方法之前执行
void main() {
  Student student = Student('yuanchen','男');
  print(student.name);
}

class Person {
  String name;
  Person(this.name);
  Person.withName(this.name);
}
class Student extends Person {
  int age;
  final String gender;
  //初始化列表的gender会在父类的super之前调用
  Student(String name, String gender) : gender  = gender ,super(name);
}

抽象类

  1. 抽象类使用abstract表示,不能直接实例化
  2. 抽象类可以没有抽象方法
 abstract class Person {
  //抽象类可以没有抽象方法
  //有一个空实现 没有抽象方法
  void run(){}
}
  1. 抽象方法不需要abstract修饰,无实现
 abstract class Person {
  void run();
}
  1. 有抽象方法的类一定要声明成抽象类
  2. 实现抽象类
void main() {
  Person person = new Student(); //多态
  person.run();
}
 abstract class Person {
  void run();
}

class Student extends Person {
  void run() {
    print('继承抽象类 实现抽象方法');
  }
}
  1. dart中的抽象类更像其他语言中的接口

接口

  1. 类和接口是统一的, 类就是接口(这点很奇怪)
  2. 每个类都隐式的定义了一个包含所有实例成员的接口
  3. 如果是复用已有的类的实现,使用继承(extends)
  4. 如果是使用已有的类的行为,使用接口(implements进行实现接口)
void main() {
  Student student = new Student();
  print(student.age); //10
}
class Person {
  String name;
  int get age => 18;
  void run() {
    print('person run ...');
  }
}

class Student implements Person{
  @override
  String name;

  @override
  // TODO: implement age
  int get age => 10;

  @override
  void run() {
    // TODO: implement run
  }

}
  1. 最好是使用抽象类作为接口,这样更像是一个接口,上面那种形式(类就是接口)很奇怪
void main() {
  Student student = new Student();
  student.run(); //10
}
 abstract class Person {
  void run();
}

class Student implements Person{

  @override
  void run() {
    print('Student is running ....');
  }
}

Mixins

  1. Mixins使用with连接一个或者多个minxin,似于多继承,是在多继承中重用一个类代码的方式
void main() {
  D d = new D();
  d.a(); //A.a()....
  d.b(); // B.b()....
  d.c(); // C.c()....
}

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

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

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

//同时继承ABC三个类
class D extends A with B, C{

}

  1. 作为Mixin的类不能有显式声明的构造方法
void main() {
  D d = new D();
  d.a(); //A.a()....
  d.b(); // B.b()....
}

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

class B {
  //作为Mixin 不能有显示的构造方法
//  B() {}
  void b() {
    print("B.b()....");
  }
}

//同时继承ABC三个类
class D extends A with B{

}

  1. 作为Mixin的类只能继承自Object 不能有其他继承
    4.Mixin 既能实现多继承也能实现组装 将多个不同功能的类 组装成一个最终类
abstract class Engine {
  void work();
}
class OilEngine implements Engine {
  @override
  void work() {
    print('Work with oil');
  }

}

class ElectricEngine implements Engine {
  @override
  void work() {
    print('Work with Electric');
  }
}

class Tyre {
  String name;
  void run() {}
}

//使用电的汽车
class Car = Tyre with ElectricEngine;
//使用油的汽车
class Bus = Tyre with OilEngine;

//上面两种方式其实的实现如下
//这种其实就是多继承 然后再继承的基础上再实现当前类的行为 两种不同的用法
class Car1 extends Tyre with ElectricEngine {
  String color;
}

  1. 我感觉这个东西强的一批, 可以把不同的业务模块拼到一起

操作符覆写

  1. 覆写操作符需要再类中定义
//覆写操作符的格式
//返回值 operator 要覆写的符号 (参数列表) {
  //方法体
//}

void main() {
  Person person1 = Person(18);
  Person person2 = Person(20);
  //对象是没有大于符号(>)的 但是可以通过重写大于符号来实现对象之间的比较  
  print(person1 > person2); //false

  //对象访问属性或者方法是使用点(.)的形式访问,如果想通过['age']的形式访问 可以通过重写操作符的形式实现
  print(person1['age']);
}

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