Dart 点将台 | const 关键字

1.对 const 的认知

80% 的人对 const 的认知,只停留在 const 修饰的量不可以被修改,这一条。甚至有些人对什么时候可以用 const 修饰还比较模糊。首先看一下内置的类型变量对象的构造,如 intdoubleString ,这些类型的 对象 可以使用 const 修饰。一旦被 const 修饰的量,就无法再做更改。

2.何时何处可以用 const 修饰

下面先看一下,什么时候能用 const,什么时候不能用 const。如下 Person 类中有一个 name 成员。对于一个 Person 对象而已,是不能使用 const 修饰的。错误原因,红线处也有提示:

[1]. 无 const 构造方法创建的对象,无法使用 const 修饰。 

既然提示说,想要 const 修饰对象,其对应构造需要为 const ,那就在构造前加个 const 呗。这时你会发现上面的声明不飘红了,但下面的 const 修饰的构造方法飘红了。提示说 :

[2].使用 const 构造方法的类中,其所有成员属性必须以 final 修饰。

所以这时将 namefinal 修饰,就可以不报错。从上面两点可以得出一个推论:

[推论]: 使用 const 修饰的对象,其各属性也是无法修改的。

总结一下 const 可修饰的位置:在声明变量时,可以将 const 放在前面,也可以将 const 放在等号后,修饰构造函数; const 可作为修饰符,修饰构造函数。

3.const 在类中的注意点

[3]: const 构造方法不能有方法体。

[4]: 类中的 const 修饰的成员,必须为 static 静态的。

你在源码中可以看到,使用 const 修饰的成员,都会由 static 修饰。

4.const 的价值

如下: a ,b 对象 声明为 const, c 对象 使用普通构造,可见 a 和 b 是全等的,由于没有在 Person 类中重写 == 运算符,则说明这两个量用的是 同一块内存地址 。这样的好处在于,即使在代码中用了 10000 次 const Person(name: 'toly') ,都是同一对象,都是同一块内存空间。这便是 const 常量的优势,该对象在代码 编译期间 就已经确定的。

而对于非 const 创建的对象,用了 10000 次 Person(name: 'toly') ,就是创建了 10000 个对象,每个对象都占着一份空间。所以这样看来,对于一些不变的常量,使用 const 修饰是很有价值的。

main() {
  Person a = const Person(name: 'toly');
  Person b = const Person(name: 'toly');
  Person c = Person(name: 'toly');
  print(a == b); // true
  print(a == c); // false
}

5. const 常量在 Flutter 中的使用

这时,你再反观 Flutter 中的一些东西,就会有更多的感悟,比如 Text 组件的构造器使用了 const 修饰,就说明 Text 对象可以使用 const 进行修饰,并且 Text 类中的所有属性都必须为 final。

这样,对应不变的文字信息,可以使用 const 进行修饰,这样,下次 build 时这个量就可以直接使用,而非构造新的 Text 对象。Flutter 中的很多组件和属性都有 const 构造,如果它们是不变的,最好使用 const 修饰。

const Text('张风捷特烈')

但要注意:const 是编译期常量,你不能在其中使用运行时的计算,这样编译是无法通过的。

6.关于const 的层级

如下, Padding 及其属性都是常量,作用两边,是否等价?是否需要一一加 const

这里来做一个小实验,在 Position 类中添加 Position 属性。 ab 对象构造如下,a 只用一个 const 修饰,b 每个对象都用 const 修饰。结果显示, a 和 b 是相等的 , a.positionb.position 也是相等的。这就说明,左右写法是一致的,所以没必要每个对象上都加 const 修饰。

main() {
  Person a = const Person(
    name: "张风捷特烈",
    position: Position(
      position: 4
    )
  );

  Person b = const Person(
      name: "张风捷特烈",
      position: const Position(
          position: 4
      )
  );

  print(a == b); // true
  print(a.position == b.position); // true
}

class Person {
  final String name;
  final Position position;
  const Person({this.name = '张风捷特烈',this.position});
}

class Position{
  final int position;
  const Position({this.position = 0});
}

越上层使用 const 要求是更严格的,其下都必须都是常量才可以。如下,TextStyle 中 color 使用了 withOpacity 方法,是运行时的,所以构造出的 TextStyle 就不是常量,最上层的 Padding 自然也就不能使用 const 修饰。

同理这里 Text 也无法使用 const 修饰,所以,只能将 const 给 EdgeInsets.all(8.0) 修饰。也就是说,能用 const 修饰的尽量用 const 修饰。

关于 const 在用法上的一些细节点,就讲到这里,FlutterUnit 之前没太注意这方面,可以好好优化一波了。现在你也该想一想,你的常量 const 了吗 ~

下面小结一下:

[1]. 无 const 构造方法创建的对象,无法使用 const 修饰。 
[2]. 使用 const 构造方法的类中,其所有成员属性必须以 final 修饰。
[推论]: 使用 const 修饰的对象,其各属性也是无法修改的。
[3]. const 构造方法不能有方法体。
[4]. 类中的 const 修饰的成员,必须为 static 静态的。
[5]. const 构造方法中传入的值必须是 const 对象。