简体   繁体   中英

dart + flutter: why doesn't the compiler recognize type mismatches

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?

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM