Dart语言之从入门到放弃(万字长文)

17,743 阅读21分钟

前言

前一段时间系统学习Flutter时总结了一些Dart的基础知识,东西挺多的,建议收藏慢慢看😂,如果对你有帮助的话别忘了点赞关注啊。先放一个之前一篇文章的链接,看了之后应该可以让你对Dart的异步有更深入的了解:那些你不知道的Dart细节之带你透彻理解异步。这是我前段时间学习Flutter写的Flutter版的玩安卓,感兴趣的可以去看看,很适合Flutter初学者:历时三天,完成了Flutter版本的玩安卓。 好了,废话不多说,开始进入正题吧,下面我将会从Dart的变量、内置类型、函数(方法)、流程控制语句、异常处理、类、泛型、库等方面进行描述,准备好了吗?要开车了,请系好安全带🐶,车速较快,请不要中途下车!!!

变量

Hello World

学习一门语言一般都要从打印“Hello World”开始,那么就来看一下在Dart中应该怎样打印:

void main(){
  print("Hello World");
}

和Java类似,Dart执行也需要一个main方法,打印就要比Java稍微简单点了,直接进行Print就可以了。这里需要注意的是:方法如果没有返回值的情况下可以省略void

变量的声明

真正要进入主题了。。。在Dart中变量的声明有三个关键字:var、dynamic和Object,三个关键字的使用情况各有千秋。下面来分别说一下:

var

首先说一下var关键字,了解过Kotlin的应该对这个关键字很熟悉,在Kotlin中var表示一个可变的变量(val表示不可变的,相当于final,这里不赘述),在Dart中,var如果没有初始值,可以变成任何类型,但如果var有初始值,那么类型将被锁定。这是什么意思呢?看一下下面这段代码你就明白了:

main(){
  //print("Hello World");
  var data;
  data = 'zhujiang';
  data = 123;
  print(data);
}

var在没有初始值的时候是可以随意指定类型的,例如上面的代码,可以赋值为字符串,也可以赋值为int。这里要注意:Dart中字符串可以使用单引号也可以使用双引号。

上面代码没有赋初始值,下面赋初始值再看一下:

main(){
  //print("Hello World");
  var data = 1;
  data = 'zhujiang';
  data = 123;
  print(data);
}

大家可以看到,如果有初始值,就不可以改变类型了,正好印证了上面我们所说的类型被锁定。

Object

这个关键字大家应该都很熟悉,Java中的所有对象的父类就是Object。在Dart中dynamic指动态类型,会在编译阶段检查类型。Object和var不同,即使赋了初始值,也同样可以指定别的类型:

  Object data2 = 1234;
  data2 = 'dongnao';
  print(data2);

上面所说的会在编译阶段检查类型指的是如果你调用一个本身没有的方法,可以直接报红,提醒你编写错误(var也是在编译阶段检查类型)。

dynamic

我在学习Dart之前没有在别的语言见过这个关键字,对我而言比较陌生,这个关键字和Object非常相似,唯一的区别就是在编译阶段不检查类型。和Object恰恰相反,如果你调用一个本身没有的方法,不会报错,但是当你运行的时候才会抛异常。

变量的默认值

这个本来不想说的,但是发现和之前所学的不太一样,还是说一下吧。

在Dart中,没有初始化的变量自动获取一个默认值为null

一切皆对象,对象的默认值为null

只要记着上面两句话就可以了,不要被之前的Java所禁锢,比如之前的Boolean值默认会是false,但是在Dart中的bool值默认也是null,记着,一切皆对象,对象的默认值为null

变量 final和const

final大家很了解,Java中经常使用,const相对就比较陌生了,下面说一下他们的共同点:

  • 声明的类型可省略

      final fVariable1 = 'zhujiang';
    //  final String fVariable1 = 'zhujiang';
      const cVariable1 = 'zhujiang';
    //  const String cVariable1 = 'zhujiang';
    
  • 初始化后不能再赋值

    fVariable1 = 'demo';
    cVariable1 = 'demo';
    

    上面初始化之后,这里就不可以赋值了,如果赋值即会报错

  • 不能和var同时使用

    final var fVariable1 = 'zhujiang';
    const var fVariable1 = 'zhujiang';
    

    和var同时使用的话也会报错。

下面再看一下他们的不同点:

  • 类级别常量,使用static const

    static const int monday = 1;
    

    这里大家可以参照一下系统中的类,例如DateTime。

  • const可使用其他const 常量的值来初始化其值

    const width = 100;
    const height = 100;
    const square = width * height;
    
  • 使用const赋值声明,const可省略

    const List clist = [1, 2, 3];
    //  const List clist = const [1, 2, 3];//dart 2之前,const赋值必须用const声明
    
  • 可以更改非final、非const变量的值,即使曾经具有const值

    var varList = const [1, 2, 3];
    final finalList = const [1, 2, 3];
    const constList = [1, 2, 3];
    varList = [1];
    //  constList = [1];
    //  finalList = [1];
    
  • const导致的不可变性是可传递的

    final List ls = [1, 2, 3];
      ls[1] = 4;
      print(ls);
      const List ls1 = [1, 2, 3];
    //  ls1[1] = 4;
    
  • 相同的const常量不会在内存中重复创建

      final finalList1 = [1, 2];
      final finalList2 = [1, 2];
      print(identical(finalList1, finalList2)); //identical用于检查两个引用是否指向同一个对象
    
      const constList1 = [1, 2];
      const constList2 = [1, 2];
      print(identical(constList1, constList2)); 
    

    上面代码的运行结果是false和ture,也印证了上面所说的。

  • const需要是编译时常量

    final DateTime finalDateTime = DateTime.now();
      //  const DateTime constDateTime = DateTime.now();//DateTime.now() 是运行期计算出来的值
    

小总结

本小节简单说明了在Dart中变量的一些细节问题,如有补充或者错误可直接在评论区说出来,不要给我面子😂。

内置类型

概述

在Dart中,内置类型有以下几种:Numbers 数值、Strings 字符串、Booleans 布尔值、Lists 列表(数组)、Sets 集合、Maps 集合、Runes 符号字符、Symbols 标识符

num, int, double

int和double大家都很熟悉,int还是整数值,double是64-bit双精度浮点数。这里需要着重看一下numint和double是num的子类。剩下的就没什么好说的了,其他的使用方法和Java基本一样,只是可以直接定义num类型的数据,剩下的大家直接看下面的代码应该就明白了:

  int i = 1; //整数值
  double d = 1.0; //double  64-bit (双精度) 浮点数
  int bitLength = i.bitLength;
  print('bitLength: ${bitLength}'); //bitLength判断int值需要多少bit位
  double maxFinite = double.maxFinite;
  print('maxFinite: ${maxFinite}'); //maxFinitedouble的最大值
  //int和double都是num的子类
  num n1 = 1;
  num n2 = 1.0;
  //支持十进制、十六进制
  int i1 = 0xfff;
  //科学计数法
  double d1 = 1.2e2; //120.0
  //转换
  //String->int
  int i2 = int.parse('1');
  double d2 = 1; //当double的值为int值时,int自动转成double
  print('d2: ${d2}');
//  int i2 = int.tryParse('1.0');//返回null

String

  • Dart 字符串是 UTF-16 编码的字符序列,可以使用单引号或者双引号来创建字符串

    main(){
      String a = 'zhujiang';
      String b = "zhujiang";
    }
    
  • 可以使用三个单引号或者双引号创建多行字符串对象

    String c = "dd"
        "sssdffg"
      "vftgt";
      String d = '''ssss
      fffffffgg
      grrrr''';
    
  • 可以使用 r 前缀创建”原始raw”字符串。

    String e = '''ssss
      fff\nffffgg
      grrrr''';
      String f = r'''ssss
      fff\nffffgg
      grrrr''';
      print(e);
      print(f);
    

    这里需要说明一下,如果使用r前缀,即是原始字符串,\n换行会直接打印出来而不会实现换行,下面是执行结果:

    lib/2-type.dart: Warning: Interpreting this as package URI, 'package:darttest/2-type.dart'.
    ssss
      fff
    ffffgg
      grrrr
    ssss
      fff\nffffgg
      grrrr
    
  • 可以在字符串中使用表达式: ${expression},如果表达式是一个标识符,可以省略 {},如果表达式的结果为一个对象,则 Dart 会调用对象的 toString() 函数来获取一个字符串。

    print("object:$c");
    

这里既然已经说到String了,那就再说一下StringBuffer吧,使用方法和Java一样,但是可以省略new关键字,还可以进行链式调用:

  StringBuffer stringBuffer = StringBuffer();
  stringBuffer..write("sss")..write("ssss");

bool

这里的bool相当于Java中boolean,这里只需要记着bool对象未初始化的默认值是null(上一篇文章中提到过)

List

  • Dart中可以直接打印list包括list的元素,List也是对象(java中直接打印list结果是地址值)。

    print(List)
    

    这里会直接输出List。

  • Dart中List的下标索引和java一样从0开始

  • 和java一样支持泛型。

  • 有增删改查,支持倒序,自带排序、洗牌,可使用+将两个List合并

      //常用方法 增删改查,排序,洗牌,复制子列表
      var list4 = [];
      //增
      list4.add(1);
      print('add 1 :${list4}');
      //删
      list4.remove(5);
      print('remove 5 :${list4}');
      list4.removeAt(2);
      print('remove at 0 :${list4}');
      //改
      list4[4] = 5;
      print('update list4[4] to 5 :$list4}');
      //range
      list4.fillRange(0, 3, 9);
      print('fillRange update list4[0]-list4[2] to 9 :$list4}');
      Iterable getRange = list4.getRange(0, 3);
      print('getRange list4[0]-list4[2] :$getRange}');
      //查
      var contains = list4.contains(5);
      print('list4 contains 5 :${contains}');
      var indexOf = list4.indexOf(1);
      print('list4 indexOf 1 :${indexOf}');
      int indexWhere = list4.indexWhere((test) => test == 5);
      print('list4 indexWhere 5 :${indexWhere}');
      //排序
      list4.sort();
      print('list4 sort :${list4}');
      //洗牌
      list4.shuffle();
      print('list4 shuffle :${list4}');
      //复制子列表
      var list5 = list4.sublist(1);
      print('sublist(1) list5 :${list5}');
      //操作符
      var list6 = [8, 9];
      print('list6 :${list6}');
      var list7 = list5 + list6;
      print('list5 + list6 :${list7}');
    

    这里的代码比较长,但是很好理解,就是一个集合的增删改查和一些比较常用的方法,大家可以自己手写一下加深理解。

Map

map这里就不多说了,和Java类似

Set

Set这里其实也和Java差不多,但是有几个地方需要说明一下:

  • set1.difference(set2):返回set1集合里有但set2里没有的元素集合
  • set1.intersection(set2):返回set1和set2的交集
  • set1.union(set2):返回set1和set2的并集
  • set1.retainAll():set1只保留某些元素(要保留的元素要在原set中存在)

这几个方法非常好用,简单写一下伪代码大家看一下吧:

  var difference12 = set1.difference(set2);
  var difference21 = set2.difference(set1);
  print('set1 difference set2 :${difference12}'); //返回set1集合里有但set2里没有的元素集合
  print('set2 difference set1 :${difference21}'); //返回set2集合里有但set1里没有的元素集合
  var intersection = set1.intersection(set2);
  print('set1 set2交集 :${intersection}'); //返回set1和set2的交集
  var union = set1.union(set2);
  print('set1 set2并集 :${union}'); //返回set1和set2的并集
  set2.retainAll(['aa', 'flutter']); //只保留(要保留的元素要在原set中存在)
  print('set2只保留aa flutter :${set2}');

Runes

表示符文的意思,用于在字符串中表示Unicode字符。使用String.fromCharCodes显示字符图形。如果非4个数值,需要把编码值放到大括号中。

  Runes runes = new Runes('\u{1f605} \u6211‘);
  var str1 = String.fromCharCodes(runes);  
  print(str1);

直接这样写就可以了,下面看一下打印效果:

😅 我

直接可以打印出表情,有表情需求的可以直接使用Runes。

Symbol

Symbol标识符,以前主要是反射使用,但是现在mirrors模块已经被移除,所以没啥大用了,大家只要知道有这么一个内置类型就可以了。

方法

方法这一块很重要,写代码离不开方法啊,继续开车,抓稳了!

定义

  • 可在函数内定义

    void main(){
      void test(){
      }
    }
    

    和Java不同,Dart中的函数可以定义在函数内部(和Java的匿名内部类别搞混了)

  • 定义函数时可省略类型(不建议)

    main(){
      test(){
        // return null;
      }
    }
    

    上面的函数可以写成下面这样,Dart中函数是Function类型的对象。所有的函数都返回一个值。如果没有指定返回值,则 默认把语句 return null; 作为函数的最后一个语句执行。

  • 支持缩写语法 => (Kotlin是用=来实现)

    main(){
      print(test1());
    }
    test()=> "zhujiang";
    String test1(){
      return "zhujiang";
    }
    

    上面代码中的test()和test1()效果是一样的,“=>”相当于大括号和return

可选参数

既然是函数,那么肯定要传参,我在经过Java构造方法的摧残之后,看这个功能的时候泪流满面😭。为什么会这样呢?下面先来看一个咱们的Java类的构造函数:

public class Test {

    private String name;
    private int age;
    private String address;

    public Test(String name, int age, String address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }
    
    public Test(String name, String address) {
        this.name = name;
        this.address = address;
    }

    public Test(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Test(String name) {
        this.name = name;
    }
}

一个非常简单的构造方法,只是想要传参数形式多一点,竟然需要写这么多重载方法。。。那么下面咱们来看一下拿Dart写的同样功能的代码:

class Test{
  String name;
  int age;
  String address;
  Test(this.name, {this.age, this.address});
}

What?竟然可以这样?是的,可以这样。下面来详细说一下函数的可选参数。

可选命名参数

其实上面构造方法我使用的就是可选命名参数,写的时候其实很简单,只需要加上大括号就行。下面是使用方法:

main() {
  print(add(a: 1, b: 3));
}

int add({int a = 1, int b}) {
  return a + b;
}

上面的add方法调用的时候可以不填参数,也可以填任何一个参数,也可以将参数都填上。这里要注意:

  • 如果a不填的话,默认值就是1,如果b不填,默认值就是null。
  • 传参数的时候一定要记得写a:,不然会报错。

可选位置参数

和上面的可选命名参数不同,上面的函数如果想调用,必须命名再加上冒号才能使用,可选位置参数就不需要了。这里和上面一样,默认值可加可不加:

int add2(int a,[int b = 2,int c = 3]) {
  return a + b;
}

下面是上面add2()方法的几种调用方式:

  print(add2(1));
  print(add2(1,2));
  print(add2(1,1,3));

注意

上面的可选命名参数和可选位置参数,可使用list或map作为默认值,但必须是const。

int add3({List a = const [1,2,3], int b = 2}) {
  return b;
}

匿名函数

可赋值给变量,通过变量调用。可在其他函数中直接调用或传递给其他函数。匿名函数分为无参匿名函数和有参匿名函数

  • 无参匿名函数

    var printFun = () => print("**无参匿名函数**");
    
  • 有参匿名函数

    var printFun2 = (name) => print("**有参匿名函数 $name**");
    printFun2("sss");
    

这里还有一个小的知识点可以通过()调用,不推荐。

(() =>print("***可以通过()调用,不推"))();

闭包

这里感觉没什么好说的,大家直接看代码应该就可以理解:

Function makeAddFunc(int x) {
  x++;
  return (int y) => x + y;
}
var makeAddFun = makeAddFunc(11);
print(makeAddFun(10));

就是创建一个方法,返回的也是一个方法,可以再继续调用。

函数别名

typedef给函数起一个别名,使用比较方便。例如定义一个方法的回调,直接使用别名定义。没返回值,则只要参数匹配就行了,如果定义了返回值,则返回值不一样会报错。

typedef Fun1(int a, int b);
typedef Fun2<T, K>(T a, K b);
int add(int a, int b) {
  print('a + b');
  return a + b;
}
class Demo1 {
  Demo1(int f(int a, int b), int x, int y) {
    var sum = f(x, y);
    print("sum1 = $sum");
  }
}
class Demo2 {
  Demo2(Fun1 f, int x, int y) {
    var sum = f(x, y);
    print("sum2 = $sum");
  }
}
class Demo3 {
  Demo3(Fun2<int, int> f, int x, int y) {
    var sum = f(x, y);
    print("sum3 = $sum");
  }
}

上面代码就是定义函数别名的方法,下面是调用方法:

Fun1 fun1 = add(11, 12);

操作符、流程控制语句、异常

这一小节看着东西应该挺多,其实很简单,好多都和Java基本一致。

操作符

上面这幅图中就是Dart中的操作符,标为黑色的操作符和Java中使用基本一样,这里也就不过多赘述,咱们来仔细看看标红Java中没有的操作符:

  • 后缀操作: ?.

    如果写过Kotlin的话,可以跳过这段了,这里的问号点和Kotlin中基本一致,都是为了判空而出现的,那么下面来看一下使用方式吧:

    main() {
      String a;
      print(a.length != null ? a.length : '');
    }
    

    上面就是咱们平时写的代码,一个三目表达式来进行判空。但是在Dart中可以不这样写,可以直接使用问号点。

    main() {
      String a;
      //print(a.length != null ? a.length : &#39;&#39;);
      print(a?.length);
    }
    

    是不是很方便?以后判空可以直接使用**"?."**了

  • 除(取整):~/

    这个操作符其实很简单,只是对数字进行取整,下面是实例代码:

    print(1/2);
    print(1~/2);
    

    上面代码的输出值是0.5和0

  • 类型操作:as

    这个和Kotlin中的as也基本一样,是对数据进行类型转换,很简单,直接看代码:

    num n = 1;
    int n2 = n as int;
    
  • 类型操作: is

    num n1 = 1.0;
    if(n1 is int){
      print("int");
    }else{
      print("double");
    }
    
  • 类型操作:is!

    从名称上就可以知道和上面的is正好相反,就不写代码验证了。

  • 判空:??

    这个上面也写出来了,意思就是判空,下面直接看一下使用方法吧:

    bool aaa;
    aaa = aaa ?? false;
    
  • 级联:..

    这个其实就是链式调用,之前的文章中写过,当时的例子是StringBuffer:

     StringBuffer stringBuffer = StringBuffer();
     stringBuffer..write("sss")..write("ssss");
    

操作符就说到这里吧,掌握好操作符在写代码的时候会事半功倍的,希望大家都能掌握好。

流程控制语句

这个,怎么说,稍微提一下吧,这个是基础的基础,Dart中的流程控制语句和Java基本一样,直接进行使用就行。

不是不想写,if语句、for循环、while循环、brake、continue、switch、case,没了,这就是Dart的流程控制语句,如出一辙。跳过。

异常

Dart 提供了 Exception 和 Error 类型, 以及一些子类型。还可以定义自己的异常类型。但是,Dart 代码可以抛出任何非 null 对象为异常,不仅仅是实现了 Exception 或者 Error 的对象。

下面这幅图是Dart中的Exception

下面这幅图是Dart中的Error

抛出异常

  • 这个和Java中写法一样,所有的 Dart 异常是非检查异常。 方法不一定声明了他们所抛出的异常, 并且你不要求捕获任何异常。

    // 抛出Exception 对象
    // throw new FormatException(‘格式异常');
    
    // 抛出Error 对象
    // throw new OutOfMemoryError();
    
  • Dart 代码可以抛出任何非 null 对象为异常,不仅仅是实现了 Exception 或者 Error 的对象。

    // 抛出任意非null对象
    // throw '这是一个异常';
    

捕获异常

  • 所有的 Dart 异常是非检查异常。 方法不一定声明了他们所抛出的异常, 并且你不要求捕获任何异常。可以使用on 或者 catch 来声明捕获语句,也可以 同时使用。使用 on 来指定异常类型,使用 catch 来 捕获异常对象。

     try {
        throw new OutOfMemoryError();
      } on OutOfMemoryError {
        print('没有内存了');
      }
    
  • catch() 可以带有一个或者两个参数, 第一个参数为抛出的异常对象, 第二个为堆栈信息 (一个 StackTrace 对象)。

      try {
        throw new OutOfMemoryError();
      } catch (e, s) {
        print('Exception details: $e');
        print('Stack Trace: $s');
      } finally {
        print('end');
      }
    
  • 可以使用rethrow把捕获的异常重新抛出。

     try {
        throw new OutOfMemoryError();
      } catch (e, s) {
        print('Exception details: $e');
        print('Stack Trace: $s');
        rethrow;
      }
    

类的点点滴滴

前几小节分别讲解了Dart中的变量、内置类型、函数(方法)、操作符、流程控制语句和异常,对Dart的基本语法已经有了很多的了解,那么接来说一下Dart中的类。

构造函数

前几篇文章中在讲函数(方法)的一篇中提到过,这里再说一下吧,首先来看一下Java中构造函数的写法:

class Point {
  double x;
  double y;

  Point(int x, int y) {
    this.x = x;
    this.y = y;
  }
}

下面是dart中的建议写法:

class Point {
  num x;
  num y;
  Point(this.x, this.y);
}

命名构造函数

使用命名构造函数可以为一个类实现多个构造函数, 或者使用命名构造函数来更清晰的表明你的意图。

class Point { 
  num x; 
  num y; 

  Point(this.x, this.y); 

  //命名构造函数
  Point.fromJson(Map json) { 
    x = json['x']; 
    y = json['y']; 
  } 
}

重定向构造函数

一个重定向构造函数是没有代码的,在构造函数声明后,使用 冒号调用其他构造函数。

class Point { 
  num x; 
  num y; 

  Point(this.x, this.y); 

  //重定向构造函数,使用冒号调用其他构造函数
  Point.alongXAxis(num x) : this(x, 0);
}

初始化列表

在构造函数体执行之前可以初始化实例参数。 使用逗号分隔初始化表达式。初始化列表非常适合用来设置 final 变量的值。

import 'dart:math';

class Point {
  //final变量不能被修改,必须被构造函数初始化
  final num x;
  final num y;
  final num distanceFromOrigin;

  //初始化列表
  Point(x, y)
      : x = x,
        y = y,
        distanceFromOrigin = sqrt(x * x + y * y);
}

调用超类构造函数

首先来建立一个超类(父类),下面的代码都继承自此类:

class Parent {
  int x;
  int y;

  //父类命名构造函数不会传递  
  Parent.fromJson(x, y)
      : x = x,
        y = y {
    print('父类命名构造函数');
  }
}
  • 超类命名构造函数不会传递,如果希望使用超类中定义的命名构造函数创建子类,则必须在子类中实现该构造函数。

    class Child extends Parent {
      int x;
      int y;
    
      Child.fromJson(x, y) 
    	 : x = x,
    	   y = y,
        print('子类命名构造函数');
      }
    }
    
  • 如果超类没有默认构造函数, 则你需要手动的调用超类的其他构造函数,调用超类构造函数的参数无法访问 this。

    class Child extends Parent {
      int x;
      int y;
      //若超类没有默认构造函数, 需要手动调用超类其他构造函数
      Child(x, y) : super.fromJson(x, y) {
        //调用父类构造函数的参数无法访问 this
        print('子类构造函数');
      }
    }
    
    
  • 在构造函数的初始化列表中使用 super(),需要把它放到最后。

    class Child extends Parent {
      int x;
      int y;
     
      Child.fromJson(x, y) 
    	 : x = x,
    	   y = y,
    	   super.fromJson(x, y) {
        print('子类命名构造函数');
      }
    }
    
    

常量构造函数

定义const构造函数要确保所有实例变量都是final。 const关键字放在构造函数名称之前。

class Point2 {
  //定义const构造函数要确保所有实例变量都是final
  final num x;
  final num y;
  static final Point2 origin = const Point2(0, 0);

  //const关键字放在构造函数名称之前,且不能有函数体
  const Point2(this.x, this.y);
}

工厂构造函数(Dart中的单例)

工厂构造函数是一种构造函数,与普通构造函数不同,工厂函数不会自动生成实例,而是通过代码来决定返回的实例对象。如果一个构造函数并不总是返回一个新的对象(单例),则使用 factory 来定义这个构造函数。工厂构造函数无法访问this。

class Singleton {
  String name;
  //工厂构造函数无法访问this,所以这里要用static
  static Singleton _cache; 

  //工厂方法构造函数,关键字factory
  factory Singleton([String name = 'singleton']) =>
      Singleton._cache ??= Singleton._newObject(name);

  //定义一个命名构造函数用来生产实例
  Singleton._newObject(this.name);
}

Setter和Getter

在Java中get和set方法可以直接生成,在Dart中无需自己定义。每个实例变量都隐含的具有一个 getter, 如果变量不是 final 的则还有一个 setter。可以通过实行 getter 和 setter 来创建新的属性, 使用 get 和 set 关键字定义 getter 和 setter。可以开始使用实例变量,后来可以把实例变量用函数包裹起来,而调用你代码的地方不需要修改。下面是代码实例:

class Rectangle {
  num left;
  num top;
  num width;
  num height;

  Rectangle(this.left, this.top, this.width, this.height);

  num get right => left + width;
  set right(num value) => left = value - width;
  num get bottom => top + height;
  set bottom(num value) => top = value - height;
}

抽象类(接口)

  • 不能被实例化,除非定义一个工厂构造函数。

    这个很好理解,在Java中的抽象类也同样不可实例化。

  • 抽象类通常用来定义接口, 以及部分实现。

    在Dart中没有interface这个关键字,只有abstract,所以可以使用abstract来生成接口:

    abstract class Demo{
      void zhujiang();
    }
    
    class Zhu implements Demo{
      @override
      void zhujiang() {}
    }
    
  • 抽象类通常具有抽象方法,抽象方法不需要关键字,以分号结束即可。

    其实上面代码也可以用作这个实例,只需要把implements变成extends即可:

    class Zhu extends Demo{
      @override
      void zhujiang() {}
    }
    
  • 接口方式使用时,需要重写抽象类的成员变量和方法,包括私有的。

  • 一个类可以implement一个普通类。Dart任何一个类都是接口。

  • 一个类可以implement多个接口。

最后几个说的其实有点绕,说白了就是可以实现多个,这里需要注意的是:implement只是实现接口,你需要重写接口中的方法,不然是不会执行的。还有一点是如果想extents多个类的话需要使用with关键字。说了这么多不如直接看代码,看代码应该好理解一些:

abstract class Demo{
  void zhujiang();
}

abstract class Demo2{
  void zhujiang();
}

abstract class Demo3{
  void zhujiang();
}

class Zhu extends Demo with Demo2,Demo3 implements Demo3,Demo2{
  @override
  void zhujiang() {}
}

可调用类

实现call()方法可以让类像函数一样能够被调用。这个很简单,直接上代码:

class ClassFunction {
  call(String a, String b, String c) => '$a $b $c!';
}

main() {
  var cf = new ClassFunction();
  var out = cf("aaa","flutter","damon");
  print('$out');
  print(cf.runtimeType);
  print(out.runtimeType);
  print(cf is Function);
}

下面是打印结果:

lib/5.dart: Warning: Interpreting this as package URI, 'package:darttest/5.dart'.
aaa flutter damon!
ClassFunction
String
false

Mixin

  • 子类没有重写超类A方法的前提下,如果2个或多个超类拥有相同签名的A方法,那么子类会以继承的最后一个超类中的A方法为准。
  • 如果子类自己重写了A方法则以本身的A方法为准。

在这里先不过多解释Minxin,我的理解就类似于策略模式,抽出不变的为接口,然后多实现。这里先不写了,如果有可能的话等专门写一篇介绍Dart中的Minxin的文章吧。

Dart泛型

  • Java中的泛型信息是编译时的,泛型信息在运行时是不存在的
  • Dart的泛型类型是固化的,在运行时也有可以判断的具体类型
var names = List<String>();
print(names is List<String>);//true
print(names.runtimeType); // List<String> 

在Java中,可以测试对象是否为List,但无法测试它是否是List。但是在Dart中是预编译的。

其他的泛型使用方法和Java中的类似,这里就不赘述了。

Dart中的库

这个平时用的非常多,而且非常实用,Dart有你不知道的但又非常好用的语法,继续出发!

库-使用核心库

使用import关键字来载入库:

import "dart:math";

void main() {
  print(sqrt(4));//开平方 2.0
}

对于内置的库,URI 使用特殊的 dart: scheme。对于其他的库,你可以使用文件系统路径或者 package: scheme。

库-载入第三方库

在Flutter中想使用第三方库的话需要在pubspec.yaml中的dependencies中添加依赖:

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^0.1.0
  dio: ^2.1.0

添加完成之后点击右上角的packages get进行导入依赖。下面是三方哭调用方法:

import "package:dio/dio.dart";

void main() {
  getHttp();
}

void getHttp() async {
  try {
    Response response = await Dio().get("https://www.baidu.com");
    print(response);
  } catch (e) {
    print(e);
  }
}

这里给大家一个网站,需要寻找第三方库的时候直接在里面搜索即可:pub.dev/

库-指定库前缀

在Java编码过程经常会出现一个类中调用两个类中相同名字的方法,这时就需要全限定名称来调用了,但是在Dart中是不需要的:

import 'MyLib1.dart' as lib1; 
import 'MyLib2.dart' as lib2; 

void main() {
  var myLib = lib1.MyLib();
  var myLib2 = lib2.MyLib();
}

上面代码中MyLib1.dart 和 MyLib2.dart 都有一个名字为 MyLib 的类。

库-选择性载入

  • show-只载入库的某些部分
  • hide-筛选掉库的某些部分
import 'Mylib1.dart' as lib1 show Test;
import 'Mylib2.dart' as lib2 hide Test;

var test = lib1.Test();
var lib = lib2.MyLib();

如果只使用库的一部分功能,则可以选择需要导入的 内容。

库-延迟载入

  1. 使用deferred as导入
  2. 使用标识符调用loadLibrary()加载库
import 'MyLib1.dart' deferred as lazyLib;

void main() {
  lazyLoad();
}
lazyLoad() async {
  await lazyLib.loadLibrary();
  var t = lazyLib.Test();
  t.test();
}

使用 await 关键字暂停代码执行一直到库加载完成。可提高程序启动速度。用在不常使用的功能和载入时间过长的包。

库-自定义库

  1. 首先将一个文件作为library:

    library mylib;
    
    part 'util.dart';
    part 'tool.dart';
    
    void printMyLib() => print('mylib');
    
  2. 然后使用part 把一个库分开到多个 Dart 文件中。

    part of mylib;
    void printTool() => print('tool');
    
    part of mylib;
    void printUtil() => print('util');
    
  3. import不会完全共享作用域,而part之间是完全共享的。如果说在A库中import了B库,B库import了C库,A库是没有办法直接使用C库的对象的。而B,C若是A的part,那么三者共享所有对象。并且包含所有导入。

    import 'mylib/mylib.dart';
    
    void main() {
      printMyLib();
      printUtil();
      printTool();
    }
    

总结

这一篇文章之前写了好久,本来想每个小节分成一篇文章发布的,这样就会有好几篇文章了😂,但又想了想那样不太厚道,直接全写完再发吧。看掘金中都是大佬,各种一线大厂技术、各种阿里腾讯名义的文章。。。比不了比不了,只是一个小破公司的一个一直在努力前进的小码农,写的文章也许不太好,但也都算用心了,希望大家能够喜欢,最后说一句,别忘了点赞关注评论啊😂😂😂,感激不尽。