Description
First off, I totally recognize late
right now is being (in most cases?) directly lowered by the CFE, and not optimized 👍 . I'd probably still recommend using late
where it makes the code significantly cleaner, but otherwise I don't quite understand why it's not just trading an NPE on (!.)
for an equally cryptic LateInitializeErrorImpl
and a lot of runtime + download cost.
I evaluated using late
in two places today when migrating code, and decided against it in both cases:
Instance-level final
field
-// @dart=2.9
+
class NgZone {
final _outerZone = Zone.current;
- Zone _innerZone;
+ late final Zone _innerZone;
NgZone() {
_innerZone = _createInnerZone(_outerZone);
}
// ... Additional methods that access _innerZone ...
}
... and a null-check with an inlined error handler is emitted on every access point:
accessInnerZone$0: function() {
var t1 = this.__NgZone_innerZone;
if (t1 == null)
t1 = H.throwExpression(H.LateInitializationErrorImpl$("Field '_innerZone' has not been initialized."));
t1.useInstanceMethod$0();
}
Using a nullable type and a !
(and -o3
) was much more terse:
-// @dart=2.9
+
class NgZone {
final _outerZone = Zone.current;
- Zone _innerZone;
+ Zone? _innerZone;
NgZone() {
_innerZone = _createInnerZone(_outerZone);
}
// ... Additional methods that access _innerZone ...
}
accessInnerZone$0: function() {
this.__NgZone_innerZone.useInstanceMethod$0();
}
Local variable
void funcWithNullableLocal(List<Foo> queue) {
- Foo foo;
+ late final Foo foo;
void onCallback() {
queue.remove(foo);
}
foo = Foo(onCallback);
queue.add(foo);
}
funcWithLateFinalLocal: function(queue) {
var t2, t1 = {};
t1.foo = null;
t2 = new R.funcWithLateFinalLocal__foo_get(t1);
new R.funcWithLateFinalLocal__foo_set(t1).call$1(new R.Foo(new R.funcWithLateFinalLocal_onCallback(queue, t2)));
queue.push(t2.call$0());
},
R.funcWithLateFinalLocal__foo_set.prototype = {
call$1: function(t1) {
var t2 = this._box_0;
if (t2.foo == null)
return t2.foo = t1;
else
throw H.wrapException(H.LateInitializationErrorImpl$("Local 'foo' has already been initialized."));
}
};
R.funcWithLateFinalLocal__foo_get.prototype = {
call$0: function() {
var t1 = this._box_0.foo;
return t1 == null ? H.throwExpression(H.LateInitializationErrorImpl$("Local 'foo' has not been initialized.")) : t1;
}
};
R.funcWithLateFinalLocal_onCallback.prototype = {
call$0: function() {
C.JSArray_methods.remove$1(this.queue, this._foo_get.call$0());
}
};
... compared with nullable Foo
:
void funcWithNullableLocal(List<Foo> queue) {
- Foo foo;
+ Foo? foo;
void onCallback() {
queue.remove(foo);
}
foo = Foo(onCallback);
queue.add(foo);
}
funcWithNullableLocal: function(queue) {
var foo, t1 = {};
t1.foo = null;
foo = new R.Foo(new R.funcWithNullableLocal_onCallback(t1, queue));
t1.foo = foo;
queue.push(foo);
},
funcWithNullableLocal_onCallback: function funcWithNullableLocal_onCallback(t0, t1) {
this._box_0 = t0;
this.queue = t1;
},
/cc @leafpetersen @rakudrama - I can connect with you internally this week to see how I can help further!