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

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

镇楼目录:

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

好废话不多说,直接开干。

一、一等方法对象

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 中方法可以有两种类型的参数:必需的和可选的。 必需的参数需要在参数列表前面, 后面再定义可选参数。

二、可选命名参数

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

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>   //函数,也叫方法,对象的操作;
}
复制代码

上面我们提到 方法,其实每个实例变量都会自动生成一个 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.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();
  }
}
复制代码

八、方法

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

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

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

可能小伙伴觉得有点绕:那我们总结一下。其实就两句话:

  • 单继承,多实现。
  • 继承可以有选择的重写父类方法并且可以使用super,实现强制重新定义接口所有成员。

十一、可调用的类:

如果 Dart 类实现了 call() 函数则 可以当做方法来调用。

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

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

十二、混合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实现的一些源码,从里面吸取一些正确的经验。

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值