前言

若想开端开发 flutter,那么谷歌推出的 dart 编程言语是必备的, dart根本上便是为学习java 的人量身订做的,在一些语法上有比较像 swift,能够说谷歌为了推出跨平台flutter,是花了不少心思的,究竟一个是土生土长的java,另一个也是ios端现在最新最流行的swift

从言语上来说,javaswift只要会一个,上手就和喝水相同简单,下面跟着我学习一遍吧,学完剩余的便是实战了

dart_demo事例 — 此事例能够在vscode下载code runer插件,进入代码后右键运转(留意:从文件的main函数运转)

学习参阅地址

ps:或许有人会说,你这不便是照着参阅地址,学完一遍后,自己举个例子简单翻译就结束了么?我想说,这便是我学习理解与回忆的一部分呀,另外也能够看出 dart 言语上手便是这么简单

dart 编程言语

导入一个库

导入库的时候运用 import,后面一般是 [dart:库名],一般体系会主动导入

另外对于引入自己的文件,则是依据当前路径定位文件的相对方位

./: 回来当前文件地点目录

../: 回来上一个目录

//运用库,有必要要写到最上面
import 'dart:async';
import 'dart:html';
//导入数学库
import 'dart:math';
//导入内置的文件,导入某个项目文件,运用相对路径
import './4class.dart'; 
//导入一个库,因为其调用办法和c相似,因而或许会和咱们项目中有重名的情况
//为了防止调用过错或许抵触报错问题,能够运用 import * as + 姓名 的办法导入,即模块化导入
//默许导入办法
import 'dart:convert'
//模块化导入办法
import 'dart:convert' as convert;
//以这种模块化办法导入,相当于将里边露出出来的办法给放到了自己命名的类中
//import + 库名 + as + 命名,调用办法: 类名.函数名即可调用
本来调用: jsonEncode()  ---  模块化调用: convert.jsonEncode()

常量和变量

变量都是 var润饰,许多言语常量用 let 润饰常量,这儿的 final 就相当于 let,能够给变量赋值一次, const表明只读,一般在修正阶段运用,界说时就要给值,也能够像c相同运用指定类型声明

dart在声明目标时,无需指明变量类型,体系会主动依据创立时的类型,揣度出实践类型,即类型揣度(作为参数传递时,则需求指明);在需求时,能够将 let之类的润饰符换成指定类型,以声明变量类型,也算是保存一些其他端的用户习气

ps:和 js不同,jsvar具有变量提升作用,let 相当于声明一般的局部变量,这儿的 var 声明的便是一个一般变量

//赋予一个字符串特色
const readonlyName = "我是只读的常量"; //修正期间运用,  也和 swift 中 let 相似
// final finalName = "我只能被赋值一次";//只能被赋值椅子,之后不能赋值
final finalName; //只能被赋值一次,之后不能赋值,宛如swift中的let
finalName = "我只能被赋值一次";
// static const sreadonlyName = "我是只读的类特色,只能在类中运用";
var name = "我是可更改的变量"; //变量
name = "我的内容更新了";
//也能够运用声明指定类型办法接纳变量,平时用的比较少
String a = "我是直接运用类型的办法声明";

数字number

这儿的数字其实也是目标,和 js 相似,数字有自己的一些目标办法,能进行一些数学操作

ps: 数字的类型都是小写哈,和其他的不同,感觉或许是另外一个人写的

//数字,能够运用常见的Math函数
var num1 = 10; //类型揣度,实践是int类型
var num2 = 10.2; //类型揣度,实践是double 类型
//数字能够运用abs()、ceil()、floor()等,也支撑常见的四则运算、移位、与、或、非、异或等操作
//也能够运用math数学库 import 'dart:math';
var num3 = num1 | num1 & num1 & num1;
var numa = num2.abs();
var numc = num2.ceil();
var numf = num2.floor();
var nump = pow(num2, num1); //数学库的办法
//bool类型
var boolNum = true;
boolNum = false;

字符串String

字符串的类也是一个目标,能够像其他言语相同经过 + 或许${} 拼接字符串,连一些字符串的操作姓名都相同,如下所示,列出一部分

var str = '我是一个一般的字符串';
var str1 = "我是一个没差异的双引号字符串";
var str2 = str + str1; //拼接字符串
//几能够运用三个单引号或许三个双引号,将一片多行字符串拼接起来
var str3 = ''' 
'sfasdf' 
'123123'
''';
str3 = """ 
sfasdf
123123
""";
//字符串前面加上r内部字符串不会被转义
var rstr = r'1212313 \n 123123';
//字符串中拼接其他变量,经过 ${} 包裹变量内容
var spitStr = "str的总长度是:${str.length}, str1的内容是:${str1}";
//获取子串 1-2,从1开端共 3-1=2个字符串
str.substring(1, 3);
//分割字符串为数组,其他言语中也很常见
str.split('一');
//是否包括某一个子串
final result = str.contains(str1);
//查找子串的方位
str.indexOf(str1);
//转化为大小写
str.toLowerCase();
str.toUpperCase();

字符串数字相互转化

字符串,数字中比较常见转化了,如下所示

其间 toString是各个目标中根本都存在的办法,能够转化字符串,在一些网络数据中,为了安全根本根本必调的

保存小数点也是比较常见的,有指定的办法也能防止自己写逻辑

var num1 = 10;
var num11 = 10.21312893192783;
var str1 = "10.2";
//数字转化字符串
var num2 = int.parse(str1);
var num3 = double.parse(str1);
//字符串转化成数字
var str2 = num1.toString();
var str3 = num1.toStringAsFixed(2); //保存小数点后两位

数组 List

数组在这儿类型姓名为 List不是Array), 不得不说,运用中最常见的调集便是数组了,其为有序的线性调集

//创立一个空数组,默以为任何类型,结构办法的办法,体系不引荐
var list = [];
var list1 = <int>[]; //创立只要int类型的数组
//增加元素
list.add(1);
list.add("asfsdf");
list.add("123");
list.addAll(list1); //增加数组内一切元素
//删除指定元素,或许指定索引的元素
list.remove('123');
list.removeAt(0);
//刺进的元素
list.insert(1, "刺进的元素");
//获取数量
list.length;
//内部排序
list.sort((a, b) => a > b);
//将数组元素运用分隔符拼接成字符串
var str = list.join(';');
list.reversed; //置反数组
//forin遍历数组(引荐)
for (var item in list) {
}
//内部运用的也是forin
list.forEach((element) {
});
var newList = list.map((e) => {
  e = e + "啦啦啦"
});

哈希表 Set

Set是一个哈希表,为一个无序调集,其依据内容生成哈希值,运用哈希值作为索引获取数据信息,内部具体表现是哈希表树表哈希表树表结合仍是其他,就不知道了(听说一些在运用哈希表一起,选用二分法的办法修正查询,抛弃了红黑树等树表)

特色:写入速度稍慢,读取速度快,不会存放重复数据

//创立一个 set
var sets = {};
//运用Set的结构办法创立
var sets1 = Set();
var sets2 = Set<String>();
//创立一个有值的set
var set = {1, 2, 3, '哈哈', '啦啦'};
var set1 = <int>{1, 2, 3}; //创立一个指定类型的Set调集
set.add(3);
set.add('哈哈');
//移除某一个元素
set.remove(3);
//是否包括某一个元素,此刻取出现已没必要了
set.contains(3);
set.length; //获取数量
//遍历调集(引荐)
for (var item in set) {
}
//内部运用的也是forin
set.forEach((element) {
});

key-value哈希表 Map

Map也是一个哈希表,只不过是以key生成哈希值,存放value等信息,即 key-value办法的哈希表,完成与Set相似,内部具体表现是哈希表树表哈希表树表结合仍是其他,就不知道了(听说一些在运用哈希表一起,选用二分法的办法查询,抛弃了红黑树等树表)

//创立一个空Map,留意{}代表空Set
var map = <String, String>{};
//运用Map的结构办法创立
var map1 = Map<int, String>();
//便利创立一个Map调集
var map2 = {
"key1": '哈哈',
"key2": "啦啦"
};
//赋值和获取
map["123"] = "啦啦";
var content = map["123"];
//获取另一个类型的数组,留意下标是key,而不是数组的顺序索引
map1[1] = "哈哈";
var content2 = map1[10];
//是否包括某一个元素key
map.containsKey("key");
//遍历调集(引荐)
for (var item in map.keys) {
}
//内部运用的也是forin
map.forEach((key, value) {
});

恣意类型 dynamic、Object

dynamic表明恣意类型,能够作为一个通用类型(实践运用中,建议能不运用就不适用,容易呈现隐性bug),就像typescript 中的 any 类型,java 中的 Object 类型

Objectdynamic相似,能够表明一切的目标类型,就现在所知道的,String、number等都是目标类型(还没有碰到其他非目标类型)

tipsios中的 idNSObject类型就不相同了,那里的 int,char等都对错目标类型的,因而不能替代

根本流程句子

根本流程句子包括常见的 if elsefor循环while循环do while循环switch操控句子

var a = 10;
var b = 20;
var c = "a";
var d = "b";
if (c == d ) {
//满意条件
}else {
//不满意条件
}
//for 循环遍历
for(var i = 0; i < 10; i++) {
print("哈哈哈");
}
//for in快速遍历调集
var list = [];
for(final item in list ) {
print(item);
}
//内部是for in
list.forEach((element) {
print(element);
});
//先走操控句子
while (a++ > b) {
print("a不大于b");
};
//先走内部句子,再走操控
do {
print("a不大于b");
} while(a++ > b);
var command = 'open'; // on、open、off、close、other
switch(command) {
case 'on':
case 'open': 
  print('门开着的');
  break;
case 'off':
case 'close': 
  print('门关着的');
  break;
default: 
  print('门或许被打破了,不知道算开,仍是关');
  break;
}

反常捕获try catch

编写代码时难免会碰到不可抵抗因素,例如:一些老的api在运用中会 throw 一个反常,或许网络恳求的失败或许也会抛出一个失败反常,此刻都能够运用 try-catch处理,假如想捕获指定反常,能够运用 on 关键字来处理

void test9(int num) {
  if (num > 10) throw FormatException("num不能大于10");
}
void test10() {
  try {
    test9(12);
  }catch(e) {
    print('呈现反常了:${e.toString()}');
  }
  try {
    test9(12);
  }on FormatException {
    print("依据抛出的反常类型,只捕获某一类");
  }catch(e) {
    print("捕获剩余的类型");
  }finally {
    //不管是否反常都履行
  }
}

定时器

定时器在往常开发中运用在常见不过了,这儿就直接先介绍了

下面便是运用Timer写的一个推迟定时器距离性定时器

留意:距离定时器运用完后要合适机遇开释,不然会一直履行(合适机遇能够是,某一个功用履行结束了、此模块推出了等等)

//只会回调一次的定时器
Timer(Duration(seconds: 5), () {
//履行一个5s的定时器
});
//会回调屡次的定时器
var num = 0;
Timer.periodic(Duration(seconds: 5), (timer) { 
  if (num++ > 10) {
    timer.cancel(); //取消定时器
  }
  print(num);
});

函数

函数的界说

函数的界说和 java 很像,如下所示

//无参无回来值函数
void getUserInfo() {
}
//无参的可简化回来值作用
//留意下面回来值实践默许是 dynamic
//getUserInfo1() {
getUserInfo1() {
}
//带参的函数
getUserInfoByToken(String token) {
}
//参数带?的函数,能够理解为可选参数,除了指定类型,能够传null
getUserInfoByToken1(String? token) {
}
//带参带回来值
List getUserInfiByToken1(String token) {
  return [];
}
//设置回来类型
List<String> getUserInfiByToken2(String token) {
  return ["啦啦啦"];
}

闭包(匿名函数)

在开发中,不是只会用到往常的函数,有时候运用闭包函数,将会削减工作量,且运用起来愈加便利,不管是传参仍是调用

//无参无回来值闭包函数
var block = () {
print(123);
};
block();
//带参有回来值的
var block1 = (num) {
return num + 1;
};
var result = block1(10);
print(result);
//带箭头的,简化操作,有点兼容js箭头函数的意思
var block2 = (num) => pow(num, 2);
var result2 = block2(14);
print(result2);
var block3 = (str) => {
print("哈哈哈:${str}")
};
block3("哎呀");

闭包、函数作为参数(两者相同)

闭包函数作为参数时,和正常函数相同,只要是一眼类型的都能够,传入的都是函数指针,或许闭包指针

如下所示,演示了无参函数,带参函数、闭包、一般函数之间的传递办法,其间也用 ? 表明了可选类型的声明传入

?: 为可选类型,代表指定类型或许null,例如 double? 代表的是一个浮点数 或许 null

//无参一般函数
getUserInfo() {
}
//传入无参闭包或许一般函数
requestUser(void completed()) {
  Timer(Duration(seconds: 2), () {
    completed();
  });
}
//传递闭包参数,加上?表明可选,(闭包的参数名在中间,因而?写到最终)
requestUserTokenByCompleted(void completed(String? userToken)?) {
  Timer(Duration(seconds: 2), () {
    if (completed != null) {
      completed(DateTime.now().microsecondsSinceEpoch % 5 > 2 ? 'abc' : null );
    }
  });
}
//flutter中标准写法改善
requestUser(Function() completed) {
  Timer(Duration(seconds: 2), () {
    completed();
  });
}
//可选带参类型事例
requestUserTokenByCompleted(Function(String? userToken)? completed) {
  Timer(Duration(seconds: 2), () {
    if (completed != null) {
      completed(DateTime.now().microsecondsSinceEpoch % 5 > 2 ? 'abc' : null );
    }
  });
}
//闭包函数
void main() {
    //调用无参闭包函数
    requestUser(() {
      print("调用了一个无参闭包函数");
    });
    //此刻requestUser的回调在 getUserInfo 一般函数中
    requestUser(getUserInfo);
    //调用带参闭包函数
    requestUserTokenByCompleted((token) {
      print("获取用户信息成功了,token:${token}");
    });
    requestUserTokenByCompleted((token) {
      print("获取用户信息成功了,token:${token}");
    });
    requestUserTokenByCompleted(null);
}

重名函数类型:在传递函数或许闭包函数的进程,有时候会显得类型十分繁琐,能够选用重命名的办法来简化参数类型,简化都的参数和一般类型相同,例如:double


//重命名,或许声明一个闭包类型,与往常的不相同姓名就在回来值和参数中间,作为参数也相同
typedef void blockType(int num);
//像一般变量相同调用
test(blockType block) {
    block(2);
}

运算符

赋值运算符

赋值运算符,在许多言语中都比较通用,例如 ??= *= /= += -= %= &= |= ~= <<= >>= >>>=

&=: (按位与赋值)
|=: (安按位赋值)
!=: (按位取反赋值)
<<=: (按位左移赋值)
>>=: (按位算术右移,最前面方位补零)
>>>=: (按位逻辑右移,最前面方位补符号位)

这儿首要介绍不常见的的一些

//??
var a = b ?? c //当 b存在时回来b,不然回来c
//??=
b ??= value  //假如b存在,b保持不变,相反,b为空,则将value的值给b

联系运算符

联系运算符,比较常见的便是 as、is、is!==判别就不多说了,是否相同

as: 类型转化,能够用来转化类型,例如:将声明父类的转化声明成子类类型(实践假如不是这个类型运用或许会报错)

is: 判别一个类是不是某一个类或许其子类,是则回来true,不然回来false;假如一个类完成了某一个接口 T,那么 is T 也是true

//as、is、is!
//有这么两个类学生Student承继人类Person
//如下,Student承继自Person,虽然强转指定类型,编译阶段不一定报错,实践运转没有该办法仍是会报错
var p = new Person();
var a1 = p as Student; //
a1.getName(); //假如 p实践上没有 Student的 getName办法,那么就会报错
//as
// 仅仅 强转了编译期的声明类型,编译期间不会报错了
//实践调用时,假如不是实践类依然或许会报错
//运用时,一般经过多态或许通用类型声明一个目标,能够运用as转化为实践类型运用
//is 
//判别一个类是不是某一个类或许其子类,是则回来true,不然回来false
//假如一个类完成了某一个接口 T,那么 is T 也是 true

级联运算符

..?.. 为级联运算符的两个关键字,前者是默许调动,后者是可选调用,为空则不调用

//.. 或许 ?.. 能够省掉调用,简化屡次调用办法或许变量
//对于目标存在的情况,运用..
var paint = Paint()
    ..color = Colors.black
    ..strokeCap = StrokeCap.round
    ..strokeWidth = 5.0;
//相当于
var paint = Paint();
paint.color = Colors.black;
paint.strokeCap = StrokeCap.round;
paint.strokeWidth = 5.0;
//队友有些目标或许不存在的情况,运用?.. + ..
querySelector('#confirm') // Get an object.
    ?..text = 'Confirm' // Use its members.
    ..classes.add('important')
    ..onClick.listen((e) => window.alert('Confirmed!'));
//相当于
var button = querySelector('#confirm');
button?.text = 'Confirm';
button?.classes.add('important');
button?.onClick.listen((e) => window.alert('Confirmed!'));

异步支撑 async await

这儿的异步支撑便是比较常见的 async、await

其间 async 则是让一个函数开端支撑异步操作,此刻能够理解为并没有敞开异步线程,当异步办法碰到await时,此异步办法开端被堵塞,并保存到Future目标中,放到队列中推迟履行,然后切出此函数,持续往后履行(留意:不是跳过await持续履行该函数,而是回来上一级持续履行),然后持续履行,碰到await润饰的持续堵塞保存放入队列,直到其他办法履行结束之后,然后开端拟定Future推迟队列中堵塞的内容,就这样模仿出了异步作用(因为flutter是单线程,又依据作用猜测,大致原理应该相似,不影响运用)

ps: 堵塞履行这一步实践有没有开出多线程能够自己查看一下,或许仅仅是模仿异步逻辑,相似操作体系切换cpu履行使命片段时间等,以让使命取得呼应

另外,FuturejsPromise相似,只不过没有resolve、reject,默许回来 Future参数,经过await或许 then都能够获取参数,只不过运用 await 的条件是,当前函数得异步支撑(async),而 then 不需求(另外能够结合 Completer做出相似 Promise的作用,后面会有文章介绍)

具体为什么这么说,等看完测验成果就能猜出一些了

//声明一个异步办法
Future<bool> simulateOperate() async {
  //假定这一有一些十分耗时的操作逻辑,例如对本地数据收拾
  var num = 0;
  for(var i = 0; i < 10000000; i++) {
    num++;
  }
  print('我的异步使命履行结束了--${num}');
  return true;
}
//履行一个同步办法调用,会发现,此异步使命履行正常
void test() {
  print('敞开test同步办法');
  //经过打印能够看到这儿回来的是函数指针,异步使命成果这儿现已忽略了
  //没有 await,实践堵塞了后续使命的履行,能够理解我同步履行
  var res = simulateOperate();
  print('我是test同步办法,履行结束了:${res}');
}
//有必要在有async的函数下运用 await,能够等待异步使命履行成果,另外没有回来值时能够省掉 Future
testAwait2() async {
  print('敞开testAwait2异步办法');
  var res = await simulateOperate(); //之而立回来的是true
  print('我是testAwait2异步办法办法,履行结束了:${res}');
}
//和上面的相同,只不过时运用 then
testAwait2() {
    print('敞开testAwait2异步办法');
    simulateOperate().then((res) {
        print('我是testAwait2异步办法办法,履行结束了:${res}');
    })
}
void main() {
  print("整体使命开端");
  test();
  print("第一个测验结束");
  testAwait2();
  print("第二个测验结束");
}

履行成果如下所示

//打印成果
// I/flutter ( 5664): 整体使命开端
// I/flutter ( 5664): 敞开test同步办法
// I/flutter ( 5664): 我的异步使命履行结束了--1000000000
// I/flutter ( 5664): 我是test同步办法,履行结束了:Instance of 'Future<bool>'
// I/flutter ( 5664): 第一个测验结束
// I/flutter ( 5664): 敞开testAwait2异步办法
// I/flutter ( 5664): 我的异步使命履行结束了--1000000000
// I/flutter ( 5664): 第二个测验结束
// I/flutter ( 5664): 我是testAwait2异步办法办法,履行结束了:true

看了这个成果应该知道怎么回事了吧,需求 await 的时候在声明 async函数,不然不但没有作用,还徒增代码

tips: 假如需求一起 await 多个函数或许网络恳求,那么能够运用 Future.await(Future1, Future2, ...)

Future.wait([fun1(), fun2(), fun3()]).then((List resList) {
    print(resList); 
}).catchError((error) {
    print(error);
})

枚举

一般用在类型判别是运用,其能更好数字number类型,能友爱的判别出类型的意义,进步代码可读性,且合作 switch 运用更便利,还能更好给出提示

//枚举
//调用,能够运用前面介绍的switch来完成不同情况,当然if else也能够
enum Level {
  firstLevel,
  secondLevel,
  thirdLevel,
  maxLevel
}
//枚举的运用
Level.firstLevel

类和目标

类是目标的笼统,目标是类的载体,能够理解为目标的模板规范,能够经过这个模板规范生产出相似的目标(不多解说,笼统的工作需求事例才能更好理解)

类的声明

声明一个类用 Class 关键字

//经过 class 关键字声明一个类,叫Person,为对人的部分笼统
class Person {
   String name = '';
   int age = 0;
   Person(this.name, this.age);
}
//经过 Person 界说出 张三和李四 两个人,这两个人(personA、personB)便是目标
var personA = Person("张三", 20);
var personB = Person("李四", 21);

特色

特色声明时,需求默许赋予初值,不然就设置为可选类型?,可选类型默以为 null

class Point {
  double x = 0;
  double y = 0;
  // double? x, y; //能够连续声明多个,也能够赋值
  // double x = 0, y = 0;
  final double z; //特色只能被赋值一次
}

结构函数

结构办法有多种创立办法,下面就一一介绍(留意结构办法只能有一个,命名式除外),而析构函数却没看到

留意: 假如不写结构办法,会有默许的无参结构办法(所以体系才要求一切参数有必要有默许值,假如重写结构办法,那初值就一定必要了)

class Point {
  double x = 0;
  double y = 0;
  final double z; //特色只能被赋值一次
  一般结构办法这么写
  Point(double? x, doubel, y) {
    this.x = x;
    this.y = y;
  }
  //体系给了语法糖就能够这么简化了, 留意,结构办法不能够被重写,只能写自己的
  Point(this.x, this.y, this.z); //结构办法
  //赋值的时候,以这种办法能够给剩余的赋值
  Point(this.x, this.y): z = 20;
  //命名式结构办法
  //办法: [类名].[办法名]
  //假如结构办法不传参数,或许漏传,需求在后面加上:对剩余参数赋值,如下所示
  Point.origin(): x = 0, y = 0, z = 0;
  //相当于下面的
  Point.origin(this.x, this.y, this.z);
  //factory 工厂模式
  //先运用命名式结构办法,界说一个结构函数,用来赋值
  Point.internal(this.x, this.y): z = 0;
  //在运用默许的特色,来给默许的结构办法敞开结构工厂
  factory Point(String key) {
    var pointList = pointMap[key];
    if (pointList == null) {
      return new Point.internal(0, 0);
    }
    return Point.internal(pointList[0], pointList[1]);
  }
}

各种类的的调用如下所示

void main() {
  var point0 = Point(0, 0, 0); //正常运用结构办法
  var point1 = Point.internal(10, 10); //命名式结构办法
  var point = Point("1010"); //调用工厂结构办法初始化
  point.printPoint(); //调用目标办法
  Point.sPrintPoint();//调用目标办法
}

静态办法

在一个类中声明静态办法,需求运用 static 关键字,这样该办法就能够经过类名调用

留意: static 的办法中,this 不是指向类,因而static的不能运用this

class Point {
  //声明一个函数,这儿不多介绍
  printPoint() {
    print("point");
  }
  //能够理解为类办法,需求经过类名调用,但是也不像其他言语相同,其不能与目标办法同名
  static sPrintPoint() {
    //留意static办法不想像其他言语相同运用this调用同类型办法
    print("point");
    Point.sPrintPoint2(); //不能运用this.sPrintPoint2()
  }
  static sPrintPoint2() {}
}

重写运算符

经过重写运算符,能够让一个类具有和数字相同的运算

声明办法: Point operator 运算符符号 + 参数 + 完成

class Point {
  double x = 0;
  double y = 0;
  //重写运算符,能够传入操作符右侧变量
  Point operator +(Point value) => Point.origin(x + value.x, y + value.y);
  Point operator -(Point value) => Point.origin(x - value.x, y - value.y);
}

Setter和Getter

这儿的 Setter、Getterios中的就大有不同了,ios是真的重写特色的SetterGetter办法,这儿的SetterGetter 仅仅能像特色相同调用算了

其一般用来依据当前特色虚拟出新的特色,平时运用不是必要的(也能够搞出相似特色的单例)

界说经过 get set 关键字完成,如下所示

其只能以 set 和 get 润饰办法名,其能像办法特色相同赋值和获取,但不是重写现已声明的特色字段,也不能同名
var instance = Rectangle(1, 2, 3, 4);
class Rectangle {
  double left, top, width, height;
  //能够经过static静态办法,搞一个单例
  static Rectangle get shared => instance;
  //一般运用set 和 get,重写后调用便利 看情况运用
  //下面设置一个居右侧特色
  double get right => width + left;
  set right(double value) => left = value - width;
}

笼统办法(接口与完成)

笼统办法经过 abstract关键字润饰,其便是接口,通常作为接口完成成多个相似的类

一个类完成一个接口,经过 implements 关键字即可,完成多个接口经过,离隔

//笼统办法 abstract
//其无法创立实例,且内部界说的办法一定是笼统办法
abstract class AbstractClass {
  speak(); 
}
abstract class AbstractClass1 {
  talk();
}
//完成接口
//经过完成接口,能够给让不同的类完成一致调度功用,就像多态相同
class Children implements AbstractClass {
  @override
  speak() {
    print("嘤嘤嘤");
  }
}
class Person implements AbstractClass {
  @override
  speak() {
    print("我不止嘤嘤嘤,我还哈哈哈");
  }
}

经过将变量类型声明成接口类型,能够被遵循接口的类一致调度,如下所示

speakForAll(AbstractClass p) {
  p.speak();
}
testSpeak() {
  var chi = Children();
  var per = Person();
  speakForAll(chi);
  speakForAll(per);
}

承继

经过 extends 关键字,能够承继一个类,被承继的类被称为 父类、超类,承继者被称为子类、派生类

//class 内部也能够界说接口,只不过是经过承继重写的办法来完成
//因而经过承继界说的接口也被称为隐式接口
class ParentClass {
  ParentClass();
  void sayHello() {
    print("hello");
  }
}
//承继,选用 extends
class SubClass extends ParentClass {
  SubClass();
  @override
  void sayHello() {
    // super.sayHello(); //能够经过super调用父类办法
    print("我不会sayHello");
  }
}

父类型能够用来声明子类变量,这便是多态的一种

一个父类(超类)被承继后,生成多个种类,这也是多态,例如:界说一个动物类为父类,子类为牛、马、蛇、羊,这也是多态

//这便是多态的运用之一
test(ParentClass p) {
    p.sayHello();
}
var sub = SubClass();
test(sub)
//这也是多态,子类也被成为派生类
class Animal {
    speak() {
        print("我会说话");
    }
}
class Dog extends Animal {
    @override
    speak() {
        print("汪汪汪");
    }
}
class sheep extends Animal {
    @override
    speak() {
        print("咩咩咩");
    }
}

承继并完成笼统接口

承继和完成接口并不抵触,且接口能一起完成多个,经过,离隔,如下所示

//承继并完成笼统接口,能够一起完成多个笼统接口
class B extends A implements AbstractClass, AbstractClass1 {
  @override
  speak() {
    print("我不止嘤嘤嘤,我还哈哈哈");
  }
  @override
  talk() {
    print('我现已不仅仅会嘤嘤嘤,还会正常聊天了');
  }
}

类扩展 extension

能在原有类的基础上扩展新的特色或许办法,扩展的父类,子类依然能用,反之不能

运用办法: extension + 扩展名 + on + 被扩展类名

ps:或许有人觉得扩展名没有存在必要,实践扩展名能够作为标记名,将不同功用的扩展更好区分隔,更利于阅读

extension Employee on SubClass {
  void work() {
    print("我能工作了");
  }
}
extension Sleep on ParentClass {
  void sleep() {
    print("我歇息了");
  }
}

多承继 maxin

运用maxin润饰的父类,能够让承继者们一起承继完成多承继的作用

一般类只能extends一个父类,经过 with + maxin声明类,能够完成多承继,且 maxin 润饰的类只能运用with关键字承继

被多承继父类声明办法: maxin + 类名

承继时运用办法: class ... + with + maxin类(多个,离隔)

多承继的优点不少,能够多个小功用类拼接成一个大类运用,运用十分灵活(比起扩展来说),缺陷也十分明显,太灵活了,代码不好操控,可读性下降

留意:其他的言语经过接口、协议、扩展等替代它(C++除外),扩展与其相似,而扩展却是针对固定类的

//界说一个打游戏的类
mixin Player {
  bool canPlayWangzhe = false;
  bool canPlaychiji = false;
  playGames() {
    print('在这儿,我是一名游戏玩家');
  }
//承继 A 的一起,承继了 Player,经过with能够承继多个,离隔
class maxinTestClass extends A with Player {
}
多承继类型束缚

类型束缚,即:maxin润饰的类被多承继时,只要承继了指定类,才能多承继他

声明被多承继父类办法: maxin + 类名 + on + 被束缚到类名

//界说一个打游戏的类
mixin Player {
  bool canPlayWangzhe = false;
  bool canPlaychiji = false;
  playGames() {
    print('在这儿,我是一名游戏玩家');
  }
}
//界说一个类,只能被承继B的类,多承继
mixin stu on B {
  study() {
    print('我是学生,要学习');
  }
}
//承继 A 的一起,承继了 Player,经过with能够承继多个
//承继了A,此刻不能承继Stu
class maxinTestClass extends A with Player {
}
//承继多个,承继B的一起承继Stu,player
class maxinConstraintTest extends B with Player, stu {
}

泛型

泛型能够经过一个类型 name (一般用T,也能够依据指定场景起名),能够泛指某一个设定的类型(用户传入的泛型类型)

示例:List调集,其能够设定一个指定类型,设定后,增加一切元素都只能是这个类型(这就运用泛型改装一个类)

var list = <int>[];//这个<int>,括号里边的便是泛型,也能够点进去查看,int便是用户传入的泛型类型
运用泛型改装类

在类名后面加上一个<T>的办法,能够指定一个泛型,经过该泛型,能够用来一致参数类型

//下面界说一个栈,界说泛型(命名为T),用来替代里边存放节点的类型
class Stack<T> {
  List<T> container = [];
  //传入 T 类型的参数
  push(T element) {
    container.add(element);
  }
}

调用如下,一个传入了String 类型,即 TString,另一个传入了 int, 即 Tint

var stack = Stack<String>();
stack.push("哈哈");
var stack2 = Stack<int>();
stack2.push(2);
运用泛型改装函数

在开发中,或许会封装一个支撑多种类型的处理办法,例如:往一个调集塞东西,交流两个参数内容等等

//或许会封装一个,支撑多个类型的处理办法
//单纯的运用一个泛型办法,其类型束缚也是extends
class handlerClass {
  static pushByList<T>(List<T> list, T object) {
    list.add(object);
    //debug能够打印了运转时的实践类型字符串
    // print(object.runtimeType.toString());
  }
}
泛型束缚

经过 extends 来束缚一个泛型,因而也能够理解为,此泛型承继某个类、接口

//假如想 像多承继相同约束泛型能够运用的类型怎么办
//如下所示,运用 extends 来约束类型为 int 或许为其子类
class StrStack<T extends String> {
  List<T> container = [];
  //传入 T 类型的参数
  push(T element) {
    container.add(element);
  }
}
//泛型束缚界说一个函数
class handlerClass {
  static pushByList<T extends String>(List<T> list, T str) {
    list.add(str);
  }
}

调用办法如下所示

var stack = StrStack<String>();
stack.push("哈哈");
// var stack2 = StrStack<int>();这个就报错了,函数也是一个姿态
var list = <String>[];
handlerClass.pushByList(list, "哈哈哈");

最终

跟着这篇文章,自己又学习扩展了一次,收益匪浅,也算是一份常用内容记录,假如又看到了,跟着走一遍,相信也会有所收成,至少对 dart 不在陌生不是