8

I am trying NNBD as of now and I would like to know if you can use the new keyword late and final together.

From what I understood, a late property can be set anywhere. You are basically telling the analyzer that it will not be null when used.
I think that is kinda dangerous in some situations.

So I am wondering if you can add a late final in NNBD, this would tell the analyzer that the property must be initialized within the class constructor.

There is a question similar but I guess there wasn't null safety at the time: Dart. Late initialize final variables

Jonathan
  • 4,724
  • 7
  • 45
  • 65

3 Answers3

7

You can declare a late final variable.

If you declare it with an initializer, late final foo = computeSomething();, then it is a lazy final variable. You can't assign to the variable, but its value is only computed the first time the variable is read. (In my experience, this is never the right choice for local variables, even though the language allows it. If you care about lazy initialization of a local variable, you also almost always want to know whether it was initialized, and a lazy variable doesn't give you that information. It's also confusing that the code is executed out-of-order, and it doesn't allow you to use await in the initializer expression).

If you declare a late final variable without an initializer, you are allowed to write to the variable once. Because the variable is late, the compiler won't complain about assignments at compile-time, unless it's absolutely certain that you have assigned the variable already, and only if it's a local variable (because that's the only variables that the compiler attempts to track assignments to).

If the late final variable without an initializer is an instance member of a class, that means that the class interface has a setter. You need to be very, very careful about exposing late final variables in the public API of a class. (Read: Don't do that!)

It's better to use late variables internally and guard access to the fields, so you can ensure that nobody assigns the variable twice. The goal of a late final variable is not to throw if it's assigned twice. It should never be assigned twice. It's there to allow allow code which knows for some reason that the compiler cannot comprehend, that the variable is only assigning once. So, only allow access to late final variables to code which are aware of that reason, and which maintains the invariant.

lrn
  • 64,680
  • 7
  • 105
  • 121
1

Short answer: No, you'll not get any help from the analyzer.


From the nnbd language spec:

It is an error if a top level variable or static variable with a non-nullable type has no initializer expression unless the variable is marked with a late or external modifier.

It is an error if a class declaration declares an instance variable with a potentially non-nullable type and no initializer expression, and the class has a generative constructor where the variable is not initialized via an initializing formal or an initializer list entry, unless the variable is marked with a late, abstract, or external modifier.

late final int foo; basically turns off null awareness for foo. It seems to be the equivalent of using implicitly unwrapped optionals in Swift, which can be hazardous, if you familiar with that.

https://github.com/dart-lang/language/blob/master/accepted/future-releases/nnbd/feature-specification.md

Besides that, the static analyzer does not warn you about trying to reset a late final.

Let D be a late and final local variable declaration named v. It is a run-time error, throwing an instance of LateInitializationError, to assign a value to v if a value has previously been assigned to v.

https://github.com/dart-lang/language/blob/master/accepted/future-releases/nnbd/feature-specification.md#late-fields-and-variables

Using late means you need to know exactly when things are being initialized and used.

Nico Spencer
  • 940
  • 6
  • 11
1

Yes!

You can see this pattern commonly used when initializing AnimationController.

class _MyState extends State<MyPage> with SingleTickerProviderStateMixin {
  late final AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this);
  }
}

And you can use it for lazy initialization, like:

class Foo {
  late final int i = calculate; // Initialized only when used.

  int get calculate => ...;
}
CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440