40分钟快速入门Dart基础(中)

最近时间太忙,每天加班加点,可是咱们的学习的行为不能落下。编程

小伙伴们继续跟着我一块儿来学习40分钟快速入门Dart基础(中)缓存

镇楼目录: bash

在本章咱们主要跟你们聊聊方法和类。这两大章看似很少,其实也很多,不太小伙伴们不用担忧,只要你们按照我下面的代码敲一遍,保证能掌握大部分知识点!网络

好废话很少说,直接开干。闭包

1、一等方法对象

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

2、可选命名参数

什么叫可选命名函数,其实说白了就是把方法的参数放到 {} 中就变成了可选命名参数。函数

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
}

复制代码

3、可选位置参数

什么叫可选命名函数,其实说白了就是把方法的参数放到 [] 中就变成了可选命名参数。工具

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
}

复制代码

4、默认参数

什么叫作默认参数,其实就是在定义方法的时候,可选参数可使用 = 来定义可选参数的默认值。

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
}
复制代码

5、匿名函数

首先什么叫匿名函数,简单来讲:

大多数方法都是有名字的,好比 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 过程当中经常使用的方法知识点咱们讲完了啊

其实方法内容不止这些,接下来的小伙伴能够自行挖掘:好比方法做用域等。

接下来咱们进入类讲解

6、类

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
  
}
复制代码

7、类--构造函数

Dart构造函数有种实现方式:

  • 默认构造方法
  • 命名构造方法Class.name(var param)
  • 调用父类构造方法
  • 不可变对象,定义编译时常量对象,构造函数前加const
  • 工厂构造函数:factory

默认构造函数每每也是咱们最多见的也是最简单的以下

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();
  }
}
复制代码

8、方法

上面是方法:方法是对象提供行为的函数。

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 的好处是,你能够先使用你的实例变量,过一段时间过再将它们包裹成方法且不须要改动任何代码,即先定义后更改且不影响原有逻辑。

9、抽象类、抽象方法、还有继承

实例方法、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();//输出实际类型 黄药师
}
复制代码

10、接口

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())); //输出:你好黄药师。你知道我是谁吗?
}
复制代码

这时疑问来了,接口跟继承有什么区别,不就是多继承吗? 接口的实现则意味着,子类获取到的仅仅是接口的成员变量符号和方法符号,须要从新实现成员变量,以及方法的声明和初始化,不然编译器会报错。而继承能够选择不从新实现,这是最大的区别。

可能小伙伴以为有点绕:那咱们总结一下。其实就两句话:

  • 单继承,多实现。
  • 继承能够有选择的重写父类方法而且可使用super,实现强制从新定义接口全部成员。

11、可调用的类:

若是 Dart 类实现了 call() 函数则 能够当作方法来调用。

class User {
  call(String name, int age) => '$name $age!';
}

main() {
  var c = new User();
  var out = c("黄药师",50);
  print(out); //输出:黄药师 50!
}
复制代码

12、混合Mixins

在面向对象的世界中,咱们最熟悉的莫过于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

最后附上:本人本身收集的工具库(待完成) 工具库-待完成

知乎: 如何快速掌握Dart这门语言并进阶Flutter变成大神

相关文章
相关标签/搜索