DEV Community

Osama
Osama

Posted on

Understanding final and const in Dart: A Beginner's Guide

Are you new to Dart and struggling to grasp the concepts of final and const? Don't worry! This blog post will break it down for you in simple terms, with plenty of examples and analogies to help you understand these important keywords.

Table of Contents

  1. Understanding Mutability
  2. The final Keyword
  3. The const Keyword
  4. Differences and Similarities
  5. When to Use Which Keyword

Understanding Mutability

Before we dive into final and const, let's first understand the concept of mutability. In programming, data types can be either mutable or immutable:

  • Mutable: Can be changed after creation
  • Immutable: Cannot be changed after creation

Let's look at some examples to understand this better.

Immutable Types: The Box Analogy

Imagine you have a box with a number inside. This box represents an integer variable in Dart.

int a = 5;
Enter fullscreen mode Exit fullscreen mode

In memory, this creates a "box" containing the value 5. Now, let's create another variable and assign it the value of a:

int b = a;
Enter fullscreen mode Exit fullscreen mode

Both a and b are now pointing to the same box with the value 5. But what happens if we change b?

b = 10;
Enter fullscreen mode Exit fullscreen mode

Instead of changing the value in the original box, Dart creates a new box with the value 10, and b now points to this new box. a still points to the original box with 5.

This is because integers in Dart are immutable. You can't change the value in the box; you can only create a new box with a new value.

Mutable Types: The Bookshelf Analogy

Now, let's look at a mutable type, like a List. Think of a List as a bookshelf where you can add, remove, or rearrange books.

List<int> listA = [1, 2, 3];
List<int> listB = listA;
Enter fullscreen mode Exit fullscreen mode

Here, both listA and listB are pointing to the same bookshelf. If we add a book to this shelf:

listB.add(4);
Enter fullscreen mode Exit fullscreen mode

Both listA and listB will see this change, because they're looking at the same bookshelf. Lists in Dart are mutable, so we can change their contents without creating a new list.

The final Keyword

The final keyword in Dart is used to create variables that can only be set once. Think of it as putting a lock on the variable name, not necessarily on the content.

final with Immutable Types

When used with immutable types like integers or strings, final behaves similarly to a constant:

final int a = 5;
// a = 10; // This would cause an error
Enter fullscreen mode Exit fullscreen mode

Here, a is locked to the value 5, and we can't reassign it.

final with Mutable Types

With mutable types like Lists, final prevents reassignment of the variable, but the contents can still be modified:

final List<int> listA = [1, 2, 3];
// listA = [3, 4, 5]; // This would cause an error
listA.add(4); // This is allowed
print(listA); // Output: [1, 2, 3, 4]
Enter fullscreen mode Exit fullscreen mode

Think of it as locking the bookshelf in place. You can't replace the entire bookshelf, but you can still add, remove, or rearrange the books on it.

The const Keyword

The const keyword in Dart is used to create compile-time constants. It's like freezing the variable and its contents entirely.

const int a = 5;
// a = 10; // This would cause an error
const List<int> listA = [1, 2, 3];
// listA.add(4); // This would cause an error
Enter fullscreen mode Exit fullscreen mode

With const, not only can you not reassign the variable, but you also can't modify its contents. It's like putting the entire bookshelf in a block of ice - you can't move the shelf, and you can't touch any of the books.

An interesting property of const is that identical const values share the same memory location:

const int a = 5;
const int b = 5;
print(identical(a, b)); // Output: true
Enter fullscreen mode Exit fullscreen mode

This is like having multiple signs pointing to the same frozen bookshelf, saving memory.

Differences and Similarities

Feature final const
Reassignment Not allowed Not allowed
Modification of mutable types Allowed Not allowed
Compile-time constant No Yes
Runtime value assignment Allowed Not allowed
Memory optimization for identical values No Yes

When to Use Which Keyword

  • Use final when:

    • You want to assign the value at runtime
    • You need to modify the contents of mutable objects
    • You're working with mutable objects that can't be made const (e.g., objects from external libraries)
  • Use const when:

    • The value is known at compile-time
    • You want to ensure complete immutability
    • You're defining constant values like PI or maximum values
    • You're working with widget trees in Flutter for performance optimization

Remember, when in doubt, start with final. You can always change it to const later if all the conditions are met.

By understanding these concepts, you'll write more efficient and less error-prone Dart code. Happy coding!

Additional Important Points

To deepen your understanding of final and const, let's explore some additional important points:

Initialization Timing

  1. final Variables:
    • Can be initialized at runtime
    • Perfect for values that are calculated or received during program execution
   final currentTime = DateTime.now(); // Initialized at runtime
Enter fullscreen mode Exit fullscreen mode
  1. const Variables:
    • Must be initialized with a constant value at compile-time
    • Cannot depend on any calculation or value that's only known at runtime
   const pi = 3.14159; // Known at compile-time
   // const currentTime = DateTime.now(); // This would cause an error
Enter fullscreen mode Exit fullscreen mode

Usage in Classes

  1. final in Classes:
    • Can be used for instance variables (non-static class members)
    • Useful for values that are set once per instance but may differ between instances
   class Person {
     final String name;
     Person(this.name); // name is set once per instance
   }
Enter fullscreen mode Exit fullscreen mode
  1. const in Classes:
    • Typically used for static class members or top-level constants
    • All instances of the class will share the same value
   class MathConstants {
     static const double pi = 3.14159;
     static const double e = 2.71828;
   }
Enter fullscreen mode Exit fullscreen mode

Performance Considerations

While both final and const can improve code safety, const can also provide performance benefits:

  1. Memory Optimization:
    As mentioned earlier, identical const values share the same memory location, which can save memory in large applications.

  2. Compile-time Checks:
    The Dart compiler can perform additional optimizations with const values since they're known at compile-time.

  3. Flutter Widgets:
    In Flutter, using const constructors for widgets can improve performance by reducing unnecessary rebuilds.

   // This widget will never rebuild unless forced to
   const MyWidget(text: 'Hello');
Enter fullscreen mode Exit fullscreen mode

Compile-time vs. Runtime Constants

Understanding the difference between compile-time and runtime constants is crucial:

  1. final (Runtime Constants):

    • The value is fixed at runtime
    • Can be used with values that are not known until the program runs
  2. const (Compile-time Constants):

    • The value must be known before the program runs
    • Offers stronger guarantees and potentially better performance

By understanding these additional points, you'll be better equipped to choose between final and const in various scenarios, leading to more efficient and robust Dart code.

Remember, mastering these concepts takes practice. Don't hesitate to experiment with different use cases to solidify your understanding. Happy coding!

Top comments (0)