I have an abstract class Event
and a concrete subclass that extends it called PlaceChangeEvent
. Inside an event listener, I have the following code:
void onEvent(Event event) {
PlaceChangeEvent pce = null;
if(event is PlaceChangeEvent)
pce = (PlaceChangeEvent)event; // <== error is here
else
throw new Exception("Event is not a PlaceChangeEvent.");
Place place = pce.place;
presenterProvider.display(place);
}
So if the runtime type of event
is PlaceChangeEvent
, then I need to cast the event to that type so that I can access its properties. But I'm getting a compiler error on the typecast, stating:
A value of type 'Type' cannot be assigned to a variable of type 'PlaceChangeEvent'
Where am I going wrong, and what do I need to do to fix it?
🎯 Dart is strongly-typed language. When using Firestore, a NoSQL database, be careful with accessing fields in a document to ensure it is the expected type, & to handle nulls.
Dart objects have runtimeType property which returns Type . To check whether the object has a certain type, use == operator. Unlike is , it will only return true if compared to an exectly same type, which means comparing it with its super class will return false .
Covariant is a fancy type theory term, but it basically means 'this class or its subclasses'. Put another way, it means types that are equal or lower in the type hierarchy. You are explicitly telling Dart to tighten the type checking of this argument to a subclass of the original.
In Dart
upcasts are implicit. If B
is a subclass of A
, then B b = a
(where a
is an instance of class A
) is warning free and silently casts a
to B
. In Java you would have needed to write B b = (B) a
.
the dynamic type is an always-present escape hatch. If B
and C
are not in the same hierarchy, then temporarily assigning to the dynamic type will make the cast warning-free.
B b = someB;
var tmp = b;
C c = tmp;
One can do an explicit check using is
. The is
check returns true if the object is of the right type. There are also some very simple rules that propagate is-check types. For example, if is
is used inside an if
condition, then the corresponding branch uses this information for type-warnings.
Object o;
o.foo(); // warning: foo does not exist on Object.
if (o is A) { // assuming that A contains 'foo'.
o.foo(); // warning-free.
}
One can explicitly check and throw if a type is not the expected one with as
. This operator does not throw when the left-hand side is null
.
For your example this boils down to:
No explicit check (1):
void onEvent(Event event) {
// Implicit upcast. PlaceChangeEvent is subclass of Event.
PlaceChangeEvent pce = event;
Place place = pce.place;
presenterProvider.display(place);
}
With an is-check (3):
void onEvent(Event event) {
if (event is PlaceChangeEvent) {
// Static type system propagates type. No need for variable.
Place place = event.place;
presenterProvider.display(place);
} else {
// Note: this doesn't look like an exception, but an error.
throw new Exception("Event is not a PlaceChangeEvent.");
}
}
Using as
(4):
void onEvent(Event event) {
Place place = (event as PlaceChangeEvent).place;
presenterProvider.display(place);
}
Alternatively, if you expect to receive a PlaceChangeEvent, you should simply change the type of the argument:
void onEvent(PlaceChangeEvent event) {
Place place = event.place;
presenterProvider.display(place);
}
In checked mode this will catch bad types, and in unchecked mode it will throw when accessing event.place
. This is generally the preferred way.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With