最近时间太忙,每天加班加点,可是咱们的学习的行为不能落下。编程
小伙伴们继续跟着我一块儿来学习40分钟快速入门Dart基础(中)缓存
镇楼目录: bash
在本章咱们主要跟你们聊聊方法和类。这两大章看似很少,其实也很多,不太小伙伴们不用担忧,只要你们按照我下面的代码敲一遍,保证能掌握大部分知识点!网络
好废话很少说,直接开干。闭包
Dart 是一个真正的面向对象语言,方法也是对象而且具备一种 类型 Function。 这意味着,方法能够赋值给变量,也能够当作其余方法的参数,同时也能够把方法当作参数调用另一个方法异步
import 'dart:core';
void main() {
var list = ["黄药师", "郭靖", "小龙女"];
void printElement(element) {
print(element);
}
list.forEach(printElement);
}
复制代码
在Java中若是须要可以通知调用者或者其余地方方法执行过程的各类状况,可能须要指定一个接口,其实就是咱们说的回调函数。好比View的onClickListener。而在Dart中,咱们能够直接指定一个回调方法给调用的方法,由调用的方法在合适的时机执行这个回调。编程语言
void setListener(Function listener){
listener("Success");
}
//或者
void setListener(void listener(String result)){
listener("Success");
}
//两种方式,第一种调用者根本不肯定 回调函数的返回值、参数是些什么
//第二中则须要写这么一大段 太麻烦了。
//第三种:类型定义 将返回值为voide,参数为一个String的方法定义为一个类型。
typedef void Listener(String result);
void setListener(Listener listener){
listener("Success");
}
复制代码
在Dart 中方法能够有两种类型的参数:必需的和可选的。 必需的参数须要在参数列表前面, 后面再定义可选参数。ide
什么叫可选命名函数,其实说白了就是把方法的参数放到 {} 中就变成了可选命名参数。函数
import 'dart:core';
void main() {
int add({int i, int j}) {
if (i == null || j == null) {
return 0;
}
return i + j;
}
//全参调用
print("--1--${add(i: 10, j: 20)}"); //输出:30
//不传参数(调用也是正确的)
print("--2--${add()}"); //输出:0
//选择传递参数
print("--3--${add(j: 20)}"); //输出:0
//与位置无关
print("--4--${add(j: 20, i: 10)}"); //输出:30
}
复制代码
什么叫可选命名函数,其实说白了就是把方法的参数放到 [] 中就变成了可选命名参数。工具
void main() {
int add([int i, int j]) {
if (i == null || j == null) {
return 0;
}
return i + j;
}
//全参调用
print("--1--${add(10, 20)}"); //输出:30
//不传参数(调用也是正确的)
print("--2--${add()}"); //输出:0
//选择传递参数
print("--3--${add(10)}"); //输出:0
}
复制代码
什么叫作默认参数,其实就是在定义方法的时候,可选参数可使用 = 来定义可选参数的默认值。
import 'dart:core';
void main() {
int sum([int age1 = 1, int age2 = 2]) {
if (age1 == null || age2 == null) {
return 0;
}
return age1 + age2;
}
//全参调用
print("--1--${sum(10, 20)}"); //输出:30
//不传参数(调用也是正确的)
print("--2--${sum()}"); //输出:3
//选择传递参数
print("--3--${sum(20)}"); //输出:22
}
复制代码
首先什么叫匿名函数,简单来讲:
大多数方法都是有名字的,好比 main() 或 printElement()。你能够建立一个没有名字的方法,称之为 匿名函数,或Lambda表达式 或Closure闭包。你能够将匿名方法赋值给一个变量而后使用它,好比将该变量添加到集合或从中删除。
([Type] param1, …) {
codeBlock;
};
复制代码
匿名方法看起来与命名方法相似,在括号之间能够定义参数,参数之间用逗号分割。 后面大括号中的内容则为函数体:下面代码定义了只有一个参数 item 且没有参数类型的匿名方法。List 中的每一个元素都会调用这个函数,打印元素位置和值的字符串:
import 'dart:core';
void main() {
var list = ['黄药师', '杨过', '老顽童'];
list.forEach((item) {
print('${list.indexOf(item)}: $item'); //输出:0: 黄药师 1: 杨过 2: 老顽童
});
// 若是函数体内只有一行语句,你可使用箭头语法:
list.forEach(
(item) => print('${list.indexOf(item)}: $item')); //输出:0: 黄药师 1: 杨过 2: 老顽童
}
复制代码
至此在开发flutter 过程当中经常使用的方法知识点咱们讲完了啊
其实方法内容不止这些,接下来的小伙伴能够自行挖掘:好比方法做用域等。
接下来咱们进入类讲解
Dart 是一个面向对象编程语言。 每一个对象都是一个类的实例,全部的类都继承于 Object。同时也支持面向对象的特性,好比:类、接口、抽象等。
使用class关键字声明一个dart类,后面跟类名,而且由一对花括号包围的类体 全部类都有同一个基类,Object,dart的继承机制使用了Mixin;
//伪代码
class class_name {
<fields> //字段,类中声明任何变量、常量;
<getters/setters> // 若是对象为final,或const,只有一个getter方法 这个等会咱们会有实例产生
<constructors> // 构造函数,为类的对象分配内存
<functions> //函数,也叫方法,对象的操做;
}
复制代码
上面咱们提到 <getters/setters>方法,其实每一个实例变量都会自动生成一个 getter 方法(隐含的)。 非final 实例变量还会自动生成一个 setter 方法。
class User {
var name;
var age;
User(this.name, this.age);
}
void main(){
var user =User("黄药师",50);
var _name = user.name;
var _age = user.age;
print("-----$_name$_age"); //输出:黄药师 50
}
复制代码
Dart构造函数有种实现方式:
默认构造函数每每也是咱们最多见的也是最简单的以下
class User {
var name;
var age;
User(this.name, this.age); //默认构造函数
}
复制代码
命名构造函数
Dart 并不支持构造函数的重载,而采用了命名构造函数为一个类实现多个构造函数:
class User {
var name;
var age;
User(this.name, this.age); //默认构造函数
//User(this.name); ///错误,由于不许许重载
User.age(this.age) {
name = "欧阳锋";
}
}
void main() {
var user = User.age(50);
print("----${user.name}${user.age}"); //输出:欧阳锋 50
}
复制代码
重定向构造函数
有时候一个构造函数会调动类中的其余构造函数(在Java中就是 this(...))。 一个重定向构造函数是没有代码的,在构造函数声明后,使用 冒号调用其余构造函数。
class User {
var name;
var age;
User(this.name, this.age); //默认构造函数
User.user(name, age) : this(name, age);
}
void main() {
var user = User.user("黄药师", 50);
print("----${user.name}${user.age}"); //输出:黄药师50
}
复制代码
常量构造函数
若是你的类提供一个状态不变的对象,你能够把这些对象 定义为编译时常量。要实现这个功能,须要定义一个 const 构造函数, 而且声明全部类的变量为 final。
class User {
final String name;
final int age;
const User(this.name, this.age); //默认构造函数
}
void main() {
var user = User("黄药师", 50);
print("----${user.name}${user.age}"); //输出:黄药师50
}
复制代码
工厂构造函数
当实现一个使用factory 关键词修饰的构造函数时,这个构造函数没必要建立类的新实例。例如,一个工厂构造函数 可能从缓存中获取一个实例并返回,或者 返回一个子类型的实例。(工厂构造函数没法访问 this)
//工厂构造方法 若是一个构造方法并不老是返回一个新的对象,这个时候可使用factory来定义这个构造方法。
class Logger {
final String name;
bool mute = false;
static final Map<String, Logger> _cache = <String, Logger>{};
factory Logger(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
final logger = new Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
Logger._internal(this.name);
void log(String msg) {
if (!mute) {
print(msg);
}
}
}
//调用
void main() {
//工厂
var logger = new Logger('UI');
logger.log('Button clicked');
}
复制代码
要注意的是工厂构造方法时无法访问this关键字的,因此上面就有了在类的内部这么调用构造方法的代码:final logger = new Logger._internal(name); 在上面工厂构造方法中,若是缓存中存在传入的name的key值,则取出缓存中的对应value返回。 若是缓存中没找到,就会经过命名构造方法来新建一个对象,缓存起来后返回
补充说明:借助工厂构造函数可以实现单例:(实用场景:flutter 网络请求Dio应用)
//使用工厂构造实现单例
class DioUtil {
static final DioUtil _instance = DioUtil._init();
static Dio _dio;
factory DioUtil() {
return _instance;
}
DioUtil._init() {
_dio = new Dio();
}
}
复制代码
上面是方法:方法是对象提供行为的函数。
Getters 和 Setters
Dart中每一个实例变量都隐含的具备一个 getter, 若是变量不是 final 的则还有一个 setter。能够经过实现 getter 和 setter 来建立新的属性, 使用 get 和 set 关键字定义 getter 和 setter:
class Rect {
num left;
num top;
num width;
num height;
Rect(this.left, this.top, this.width, this.height);
//使用 get定义了一个 right 属性
num get right => left + width;
set right(num value) => left = value - width;
}
void main() {
var rect = Rect(0, 0, 10, 10);
print(rect.right); //10
rect.right = 15;
print(rect.left); //5
}
复制代码
使用 Getter 和 Setter 的好处是,你能够先使用你的实例变量,过一段时间过再将它们包裹成方法且不须要改动任何代码,即先定义后更改且不影响原有逻辑。
实例方法、Getter 方法以及 Setter 方法均可以是抽象的,定义一个接口方法而不去作具体的实现让实现它的类去实现该方法,抽象方法只能存在于抽象类中,抽象类的定义跟Java的抽象类相似,就不单独介绍了。
abstract class User {
void say(); //定义一个抽象方法
}
class Person extends User{
@override
void say() {
// 提供一个实现,因此在这里该方法再也不是抽象的……
}
}
复制代码
说明:抽象类不能被实例化,除非定义工厂方法并返回子类。
abstract class User {
String name;
//默认构造方法
User(this.name);
//工厂方法返回Child实例
factory User.test(String name){
return new Child(name);
}
void printName();
}
// extends 继承抽象类
class Child extends User{
Child(String name) : super(name);
@override
void printName() {
print(name);
}
}
void main() {
var p = User.test("黄药师");
print(p.runtimeType); //输出实际类型 Child
p.printName();//输出实际类型 黄药师
}
复制代码
Dart 没有像 Java 用单独的关键字 interface 来定义接口,普通用 class 声明的类就能够是接口,能够经过关键字 implements来实现一个或多个接口并实现每一个接口定义的 API:
// Person 类的隐式接口中包含 greet() 方法。
class User {
// _name 变量一样包含在接口中,但它只是库内可见的。
final _name;
// 构造函数不在接口中。
User(this._name);
// greet() 方法在接口中。
String greet(String who) => '你好,$who。我是$_name。';
}
// Person 接口的一个实现。
class Impostor implements User {
get _name => '';
String greet(String who) => '你好$who。你知道我是谁吗?';
}
String greetBob(User person) => person.greet('黄药师');
void main() {
print(greetBob(User('欧阳锋'))); //输出:你好,黄药师。我是欧阳锋。
print(greetBob(Impostor())); //输出:你好黄药师。你知道我是谁吗?
}
复制代码
这时疑问来了,接口跟继承有什么区别,不就是多继承吗? 接口的实现则意味着,子类获取到的仅仅是接口的成员变量符号和方法符号,须要从新实现成员变量,以及方法的声明和初始化,不然编译器会报错。而继承能够选择不从新实现,这是最大的区别。
可能小伙伴以为有点绕:那咱们总结一下。其实就两句话:
若是 Dart 类实现了 call() 函数则 能够当作方法来调用。
class User {
call(String name, int age) => '$name $age!';
}
main() {
var c = new User();
var out = c("黄药师",50);
print(out); //输出:黄药师 50!
}
复制代码
在面向对象的世界中,咱们最熟悉的莫过于class、 abstract class和interface。Dart做为一门现代面向对象编程语音,在原有的特性基础上,新增了一些新的特性如:Mixins
什么是Mixins:
简单的理解,就是用来复用多个类之间的代码,减小耦合。咱们直接来看一个例子。
咱们在没有使用Mixins的从前:
假设,咱们如今正在开发一个动物大全App,咱们须要建立一个Duck类。做为一个有丰富面向对象编程经验的开发者,你天然的将全部和Duck有类似特征的抽取成一个abstract class。
/// Bird
abstract class Bird {
void shout() {
println('shouting');
}
}
/// WaterborneBird
abstract class WaterborneBird extends Bird {
void swim() {
println('swimming');
}
}
/// Duck
class Duck extends WaterborneBird {
void doDuckThings() {
shout();
swim();
println('quack quack quack!')
}
}
复制代码
很好,咱们清楚的将鸭子纳入水中生活的鸟类,加入其它的鸟类也变得很是容易。可是,如今咱们须要加入金鱼了,因而咱们和上面同样编写代码。
/// Fish
abstract class Fish {
void swim() {
println("swimming")
}
}
/// GoldFish
class GoldFish extends Fish {
void doGoldFishThings() {
swim();
pringln('zzz...');
}
}
复制代码
这是咱们发现金鱼和鸭子同样拥有swim的特性,在这个例子中是很是简单的,可是若是咱们有复杂的行为须要赋予给一个新的类,咱们就要大量编写重复的代码了。
使用Mixins
咱们声明一个Swimming的mixin:
mixin Swimming {
void swim() {
println('swimming')
}
}
复制代码
咱们可使用with关键字将mixin加入到class中,其实看到这里你可能已经回想到咱们其实可能已经用过这个with关键字了。接下来,咱们就能够对上面的代码进行改造了:
/// Bird
abstract class Bird {
void shout() {
println('游泳');
}
}
/// Duck
class Duck extends Bird with Swimming {
void doDuckThings() {
shout();
swim();
println('跳跃!')
}
}
复制代码
/// Fish
abstract class Fish {
}
/// GoldFish
class GoldFish extends Fish with Swimming {
void doGoldFishThings() {
swim();
pringln('zzz...');
}
}
复制代码
mixins弥补了接口和继承的不足,继承只能单继承,而接口没法复用实现,mixins却能够多混入而且能利用到混入类。
咱们在来看一个例子作比较:
abstract class Swimming{
void swimming(){
print("游泳");
}
}
abstract class Jump{
void jump(){
print("跳跃");
}
}
//只能单继承,若是须要Jump,只能以implements的形式
class HuangYaoShi extends Swimming implements Jump{
//实现接口
void jump(){
print("跳跃");
}
}
//可是实际上,咱们常常不须要从新实现Jump方法,复用Jump所实现的jump方法就能够了
//这时使用混合可以更加方便
class HuangYaoShi with Swimming, Jump {}
复制代码
关于Mixins,还有不少须要注意的事情,咱们虽然可使用Mixins对代码进行一些简化,可是要创建在对需求和类之间的关系准确理解的基础上。建议多去看看Flutter中使用Mixins实现的一些源码,从里面吸收一些正确的经验。
下一章也是咱们Dart最后一章咱们继续讲解Dart:异步、泛型、异常
参考:上面动物例子转载了该博主的文章: 深刻理解Dart之Mixins
最后附上:本人本身收集的工具库(待完成) 工具库-待完成