Flutter 关键字之 final 的详细介绍

454 阅读4分钟

final

一、简述

finalDart 语言 60 个关键字其中之一。它用于修饰声明的对象,例如:

final String name = 'keywords - final';
final List<int> ages = [10, 20, 30];
final Person person = Person(name:'关键字');
... // 穷举省略

二、特点

  • 必须进行初始化
    final 变量声明时必须进行初始化赋值,而且只能在声明时或构造函数中进行初始化。

  • 只能赋值一次
    一旦 final 变量被初始化后,其值就不能再更改。

  • 值可以是常量或表达式
    final 变量的值可以是编译时常量、字面量、其他final变量或由常量表达式计算得出的结果。

  • 可以是顶级变量或局部变量
    final 关键字可用于顶级变量(全局范围内)或局部变量(函数、方法范围内)。

三、优点

  • 不可变性
    final 属性一旦被赋予初始值后,其值无法再被修改。这种不可变性确保了属性的值在对象生命周期内的稳定性,避免了意外的修改或错误。

  • 安全性
    由于 final 属性的值是不可变的,它们在多线程环境下是线程安全的。不需要额外的同步机制来保护 final 属性的访问和修改,从而降低了并发编程的复杂性。

  • 可预测性
    final 属性的值在对象创建时确定,并且不能再改变。这使得代码的行为更加可预测,避免了由于属性值的变化导致的不确定性。

  • 性能优化
    在编译时,对于使用 final 修饰的属性,编译器可以进行更多的优化。由于这些属性的值是不可变的,编译器可以进行常量折叠、内联优化等,提高程序的执行效率。

  • 代码简洁性
    使用 final 关键字可以使代码更加简洁清晰,明确地指示属性的不可变性。这有助于提高代码的可读性和维护性,减少意外的错误和bug。

四、应用时机

Dart 中,final 关键字可以用于以下几种情况的应用时机:

  • 常量值
    当你需要声明一个不可变的常量值时,可以使用final关键字。这些常量值在程序执行期间是不会改变的,并且在编译时可以进行优化。例如,声明一个永远不会改变的数学常量 PI 可以使用 final double PI = 3.14159;

  • 构造函数参数
    通过在构造函数的参数前面加上 final 关键字,可以将它们作为不可变的实例属性。这样,在对象创建后,这些属性的值将保持不变。这种用法常见于需要在对象创建时传递不可变数据的情况。例如:

    class Person { 
      const Person(this.name); 
      final String name; 
    }
    
  • 局部变量
    当你有一个在其生命周期内不会改变的局部变量时,可以使用 final 关键字进行声明。这样可以提醒开发人员该变量不会被修改,并且可能触发一些编译时的优化。例如,在一个循环中,使用 final 关键字声明的局部变量可以避免重复计算,提高性能。

需要注意的是,虽然 final 关键字意味着不可变性,但并不表示它是唯一的选择。另外一个类似的关键字是 const,它用于声明编译时常量。

五、应用实例

下面是 final 的一些使用实例以及验证一些问题,如下:

  • 基础使用

    // 修饰常量
    final double PI = 3.1415926;
    // 修饰表达式
    final int age = 10 + 12;
    // 修饰类属性
    class Person {
      const Person({this.name});
      final String name;
    }
    // 修饰类
    final Person person = Person(name:'keyword - final');
    ...
    
  • 赋值后不可更改

    void main() {
      final String name = 'keyword - final';
      // bad code
      name = 'keyword - const'; 
    }
    

    上述代码语法提示 Error: The final variable 'name' can only be set once.
    上述代码运行报错提示 Error: Can't assign to the final variable 'name'.

  • 修饰对象不可变,但对象内的可变属性是可变的

    class Animal {
      Animal(this.name, this.age);
      final String name;
      int age;
      
      @override
      String toString() => 'Animal\n name:$name;age:$age';
    }
    
    final Animal dog = Animal('狗', 1);
    dog.age = 2;
    print(dog.toString()); // Animal-name:狗;age:2
    
  • 修饰的容器类对象,该对象不可变,但是容器内的元素可增加和减少以及改变

    // good code - List
    final List<int> ages = [10, 20, 30];
    ages.add(40);
    ages.removeAt(0);
    ages[2] = 110;
    print(ages); //  [20, 30, 110]
    //  bad code
    // Error: The final variable 'ages' can only be set once.
    ages = [];
    
    // 或者字典集合
    // good code - Map
    final Map<String, dynamic> map = {};
    map.putIfAbsent('key', () => 1);
    print(map);
    //  bad code
    // Error: The final variable 'ages' can only be set once.
    map = {};
    

六、总结

上面是我对 final 的一些认识,其实 final 的知识与应用还有很多其他的妙用,需要我们一起探索和发现以及使用它。来吧! 欢迎你加入探究者的行列,去领略这门语言的奇特之处。