Reputation: 30056
I'm encapsulating the data of my flutter app inside a class called AppData. It looks a bit like this:
class AppData with ChangeNotifier{
List<word.Word> _words;
UnmodifiableListView<word.Word> words;
AppData(){
// at some point this will be loaded from disk
// for now I'm just using a hardcoded list of words
_words = word.words;
// the following code would work:
// words = UnmodifiableListView(_words);
// this doesn't, but it's also not a compiler error !?
words = _words;
}
// ...
}
The AppData class keeps track of a list of words. For the consumers of AppData, this is visible as an UnModifiableListView
.
My code had a very obvious bug: I assigned _words
to words
without encapsulating the List
in the UnModifiableListView
properly.
Why is this not found by the compiler?
The types should be an obvious mismatch. From the dart docs (emphasis mine):
Dart’s type system, like the type systems in Java and C#, is sound. It enforces that soundness using a combination of static checking (compile-time errors) and runtime checks. For example, assigning a String to int is a compile-time error. Casting an Object to a string using as String fails with a runtime error if the object isn’t a string.
Update, to respond to Rémi's answer:
The error message is:
The following assertion was thrown building MultiProvider:
type 'List<Word>' is not a subtype of type 'UnmodifiableListView<Word>'
This seems like an issue of Covariance vs Contravariance.
If I know that my reference to List
actually contains an UnmodifiableListView
, then I could do the cast myself.
Why would the compiler add an implicit cast for me?
It seems to me as if this bypasses a lot of the type soundness mentioned in the docs above. Especially when I change my type hierarchies and do extensive refactoring, I rely on the compiler to tell me: You've got the types mixed up. It's very possible that their inheritance tree still reaches a common ancestor at some point. But they are definitely not the same.
At least to me, it is even more surprising, since this is not the way other "typical" OOP languages work (Java, C#, ...).
So I still wonder: Why doesn't the compiler find this, what is the reason behind this design?
Upvotes: 1
Views: 444
Reputation: 277027
What happens, in reality, is an implicit cast.
Since UnmodifiableListView
is a List
, assigning List
to UnmodifiableListView
does an automatic cast.
You can disable that on the analysis_options.yaml
like so:
analyzer:
strong-mode:
implicit-casts: false
Upvotes: 3