
Build 10x products in minutes by chatting with AI - beyond just a prototype.
Like many programming languages, Dart allows you to store data using variables. The value of a variable can typically be changed after it has been initialized, making it mutable. However, as a developer aiming to write efficient and maintainable code, you often need a way to ensure that the values remain constant, which is where Dart's final and const keywords play a crucial role.
final and const KeywordsWhen you declare a variable in Dart, you might decide its value should never change. This is when you reach for the final and const keywords, which can be used to create immutable values. But while these two keywords might seem similar at first glance, they serve different purposes, and understanding the main difference between final and const is crucial in writing efficient and maintainable code.
With the final keyword, you're telling the Dart compiler that a variable can only be assigned once and its value is determined at runtime. For example:
1void main() { 2 final String greeting = 'Hello, world!'; 3 print(greeting); // Outputs: Hello, world! 4}
In this simple example, the final variable greeting is assigned a runtime value, a string, and cannot be changed later on.
As for the const keyword, it takes immutability a step further. A const variable has to be a compile-time constant, which means its value should be known at compile time. This is not only about writing maintainable code but also can improve performance since these values are determined when your code is compiled, not as it’s running. Here's how it looks in Dart:
1void main() { 2 const String exclamation = 'Welcome to Dart!'; 3 print(exclamation); // Outputs: Welcome to Dart! 4}
The const keyword ensures that the value assigned to the exclamation variable is a compile-time constant—it can't be changed or assigned a new value after compilation.
final: A Closer LookWhen working with Dart, the final keyword is your ally in creating variables that need to be assigned once and only once. A final variable's value can be determined at runtime, which offers excellent flexibility. This allows final variables to be initialized with data that is not known at compile time, lending itself to dynamic and responsive applications.
finalThe final keyword differs from const because it allows for lazy initialization. This means you can assign a runtime value to a final variable—a value determined when the code is run. For example:
1void main() { 2 final int userAge = getUserAge(); // Assume getUserAge() retrieves the user's age at runtime. 3 print(userAge); // The actual value is determined at runtime and can't be known at compile time. 4}
In this scenario, the final variable userAge holds a value that is assigned once at runtime and cannot be reinstantiated with a new value later in the code. Using a final keyword instead of const is crucial when the value to be assigned cannot be established at compile time.
final in Class-Level VariablesAt the class level, the final keyword helps create immutable instance variables that are initialized at the constructor level and are unique for each class instance. This aspect of final variables plays a significant role in object-oriented programming and in creating immutable objects in Dart:
1class UserProfile { 2 final String name; 3 final DateTime accountCreationDate; 4 5 UserProfile(this.name, this.accountCreationDate); 6} 7 8void main() { 9 final user = UserProfile('Alice', DateTime.now()); 10 print(user.name); // Outputs: Alice 11 // user.name = 'Bob'; // This would result in a compile-time error. 12}
In the above class UserProfile, name and accountCreationDate are final variables. Once the UserProfile object is created, these properties cannot be changed. Even if a new UserProfile is instantiated, it will have its unique final properties, providing tailored immutability at the instance level.
const in DepthGoing beyond the final keyword, Dart elevates immutability to a new level with the const keyword. When you declare a const variable, you're saying the value won't change and ensuring the variable is a compile-time constant. This means its value is determined during the compilation phase and baked into the code, which can lead to significant performance improvements.
constconst variables in Dart are defined to be compile-time constants. The const keyword demands that the variable's initial value be known at compile time; thus, assigning a runtime value to a const variable is impossible. This restriction allows the Dart compiler to optimize the performance by embedding constant values directly into the code. Here's how you might use const in a Dart program:
1void main() { 2 const int birthYear = 1990; // Known at compile time. 3 print(birthYear); // Outputs: 1990 4}
In the example above, birthYear is a compile-time constant known at compile time and will never change. The Dart compiler understands this and can optimize accordingly.
const VariantsDart also allows you to define literals as const. This is particularly useful when dealing with objects like lists or maps where you want the entire data structure to be immutable:
1void main() { 2 const List<int> fibonacciNumbers = [0, 1, 1, 2, 3, 5, 8, 13]; 3 print(fibonacciNumbers); // Outputs: [0, 1, 1, 2, 3, 5, 8, 13] 4 // fibonacciNumbers[0] = 1; // This would result in a compile-time error. 5}
In the const list fibonacciNumbers, not only is the list itself a constant but each element is a compile-time constant too, illustrating how const ensures deep and comprehensive immutability.
const for Ensuring Deep ImmutabilityDeep immutability with const goes beyond just the immediate value of a variable. Ensuring that an object is transitively immutable—meaning that all of its fields and their values are also immutable—is another powerful aspect of the const keyword:
1class ImmutablePoint { 2 final int x; 3 final int y; 4 const ImmutablePoint(this.x, this.y); 5} 6 7void main() { 8 const ImmutablePoint origin = ImmutablePoint(0, 0); 9 print(origin.x); // Outputs: 0 10 // origin.x = 1; // This would result in a compile-time error. 11}
With the const keyword applied to ImmutablePoint, you guarantee that once an ImmutablePoint object like origin is created, it is completely immutable, including its properties x and y.
final and constThe decision between final and const can have implications for the performance of your Dart application. The performance benefit of using const variables comes from their nature as compile-time constants. Since const variables are known at compile time, they are evaluated once during compilation. This means the Dart compiler can optimize them away, saving memory and reducing startup time.
1const int iterations = 1000; // A compile-time constant example.
On the other hand, final variables are evaluated at runtime. They ensure a variable holds a single value after initialization, but this comes with the added benefit of allowing the assignment of values only known at runtime.
1final int startTime = DateTime.now().millisecondsSinceEpoch; // A runtime example.
final Over const and Vice Versafinal when:
1class User { 2 final String userID; 3 User(this.userID); 4}
const when:
1class Circle { 2 static const double pi = 3.14159; // A compile-time constant. 3}
The main difference between final and const can be summarized: final is about ensuring a single value post-initialization, potentially assigned at runtime, while const enforces immutability and constant values known at compile time.
Mastering the use of final and const in Dart empowers developers to write robust and efficient code. By judiciously applying final and const, you can avoid mutable state issues, streamline your codebase, and ensure that your Dart applications are built on a foundation of well-defined, immutable data.