Day04 | Dart基本介绍 - 变数宣告与基本型别

今天主要会说明 Dart 各种变数宣告的方法及 Dart 的基本型别。

变数宣告

dart主要有四种方式宣告变数

分别为 const var type final

const a = 10;
final b = 10;
var c = 123;
string d = '123' 

首先 constfinal 这两个宣告方式就跟 JS 的 const 一样是用於宣告一个「不可变的数值」。

亦即这之後不能将这些变数重新赋值。那为什麽还有分 constfinal 呢?最主要的差异是const 更为严格,它代表的是「编译时的常数」,什麽意思?

int getNumber(a){
  const b = a;
  return b;
}

void main() {
  int a = 10;
  getNumber(a);
  
}

这边会看到我在 getNumber 里使用 const 宣告b 将a的值给b ,但这无法通过 Dart的编译器。因为 const 的是要在编译期间就已经是有数值的常数。所以const a = b ; 这种直到runtime才完成初始化的事情是不被允许的。

所以 const 较常用地方是一些**「永远不变的数值」**像是 const pi = 3.14; 或者const textColor = Color.fromARGB(255, 66, 165, 245)

final 就会比较接近於 js里const的用法,就是在接下来的runtime这个数值都会是immutable。

var 就可以想成是 JS 的 let 就是可以被变更的变数。但最大的差异就是Dart在宣告完後就会进行型别推断(Type inference)意即如果 var b = 10 後不能在进行 b='123'了(可以想像成ts一样),因为在初始化後就会将 b的type推断为int了所以不能将string给b了

而直接用型别宣告的方式就跟 var 差不多一样了就不详细介绍了,就差别是一个是交由dart 推断型别一个是我们自己宣告型别。

但其实在变数宣告以及初始化还有一些细节,这部分就留到之後提到「null safety」时再来说明。

常用型别

Dart 有以下基本型别

  • intdouble
  • String
  • bool
  • List
  • Set
  • Map
  • Runes
  • Symbol
  • Null

基本的 int (整数)、 double (浮点数)、 String (字串)、bool (布林)就不多做介绍。

以下只稍微介绍一下比较常用到的 ListSetMap

List

List 就是其他语言中的阵列(Array)在Dart中的最基础形式如下:

final listA = [1,2,3,4]

在Dart里的List有提供其他建构子(constructor) .filled. generate 都是可以用来动态产生list的建构子:


final listB = List.filled(3, 1); // [0, 0, 0]
final listC = List.generate(3, (index) => index); // [0, 1, 2]

差异是 filled 每一个element都是同一个reference而 generate 不是。

listB[0].add(1);
listC[0].add(1);
print(listB); // [[1], [1], [1]]
print(listC); // [[1], [], []]

以及其中有一个控制这个List是不是可变长度的named parameters growable 的预设值不一样。但详细就不赘述了有兴趣的读者可以到官方的API文件阅读。

Set

定义为「没有索引值且不可重复的集合」

我们可以用 {} 来做初始化并用逗号分隔每一个元素:

final setA = {0, 1, 2, 3, 4};
print(setA); // {0, 1, 2, 3, 4}

也可以利用 Set.from 放入一个可迭代的值来产生 Set

final listX = [0, 1, 0, 0, 1, 2, 1, 3, 4, 5, 6, 7];
final setB = Set.from(listX);
print(setB); //{0, 1, 2, 3, 4, 5, 6, 7}

因为 Set 里并没有存放索引值,所以我们无法直接存取特定位置的值。但因为 Dart 底层实作的关系,其实还是有将 Set 的顺序存入,也因此我们迭代时是会跟初始化时的顺序一样:

setB.forEach((element) {
    print(element);
});

// 0
// 1
// ...
// 7 

Map

Map就是有key-value型式的资料结构,而且key不能重复,也因为了有了key所以我们有办法直接存取Map

final mapA = {
  'a': 1,
  'b':2,
  'c':3
};

mapA['a'] // 1

当然Map也有提供其他的constructor:

final mapB = Map.fromIterable([1, 2, 3, 4]);
// {1: 1, 2: 2, 3: 3, 4: 4}

final valueList = [0, 1, 2];
final keyList = ['z', 'x', 'c'];
final mapC = Map.fromIterables(keyList, valueList);
// {z: 0, x: 1, c: 2}

当然这些资料结构还有其他API可以介绍,但我觉得还是等到之後实际有用到时在一起介绍好了,有兴趣的读者可以先查阅Dart API的文件。

泛型

泛型(generic)最简单的解释大概就是型别有了参数

通常都是使用 <> 来实作,像是List的实作是 List<E> 而这个E就是我们可以传入的型别:

final intList = <int>[1, 2, 3, 4];
final stringList = <String>['1', '2', '3', '4'];

而当我们使用了不一样的型别时就会跳出 Error,像是我在 List<String> 里放入一个 int 就会跳出以下错误:

https://ithelp.ithome.com.tw/upload/images/20210917/20112906KUc3FvuRkQ.png

当然我们也可以运用到其他地方像是 Class 或者 Fucntion

E ientityFunc<E>(E e) => e;

class A<T> {
  T? value;
}

print(ientityFunc<int>(2)); // 2

final a = A<int>();
a.value = 'string';  // 这行会出错因为 A传入的是 int type

而关於型别有一些进阶应用像是利用 typedef 对Function的型别做更进一步的抽象

typedef Identity<E> = E Function(E e);

这行的意思就是我定义了一个型别叫做Identity,而这个型别代表的意义就是他是一个会回传
type EFunction 且只有一个type E 的参数。

class Utils<T> {
  Identity<T> ientity = (x) => x;
}

final utils = Utils<int>();
print(utils.ientity(1));

我们可以从vs code中看到因为我们传入了 int 所以 utils.ientity 也变成有一个 int 参数且会回传 intFunction

https://ithelp.ithome.com.tw/upload/images/20210917/201129065GdsLyzk9l.png


这次的内容稍微有点杂乱,这类比较基础的语法在没有实际范例来看总会有一种「到底什麽时候会用到这些语法」的感觉。

今天的程序码有放到github上,有兴趣的读者可以clone下来跑跑看。
https://github.com/zxc469469/dart-playground/tree/Day04/type

明天会开始讲到 Dart/Flutter 里最常用到的东西:class


参考资料

  1. https://dart.dev/guides/language/language-tour#built-in-types
  2. https://api.dart.dev/stable/2.14.2/dart-core/dart-core-library.html

<<:  30天轻松学会unity自制游戏-开启死亡画面

>>:  DAY 5 『 RGB调色盘 - layout ( 约束 ) 』Part4

Day 25 Redis (上)

看到这个标题,你或许会有一点疑惑,为什麽他不是以 Flask 开头?因为它是一个资料库的名称,而 F...

图的连通 (5)

9.2 找出分离点对 (Separating Pair) 如果一个点的子集合移除以後,会让图 G 变...

Day.30 讲点算法以外的东西

终於30天了(烟 第二年参赛都顺利结束,回想第一年每天都在赶QQ 今年比前年顺利!题目根本不用想要写...

Unity与Photon的新手相遇旅途 | Day24-Photon房间载入设定

今天内容为房间载入的程序码设定,明天会教大家如何测试。 ...

Day.11 「利用渐变效果,让网页不再死板!」 —— CSS Transform & CSS Transition

现在我们会使用基本的伪类选择器做效果了,但看到变化过程一闪而过、冷冰冰的,想增添更多渐变效果,让动...