Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dart runtimeType checking in switch statement

I have found some strange behaviors trying to check the runtime type of some objects in Dart. Let's make a simple example:

main(List<String> args) {
  List<Map> l1 = new List<Map>();
  l1.add(new Map<String, int>());
  List<int> l2 = ['Start', 'Stop'];
  checkType(l1.runtimeType);
  checkType(l1[0].runtimeType);
  checkType(l2.runtimeType);
}

checkType(Type type) {

  switch(type) {
    case List:
      print('it is a List!');
      break;
    case Map:
      print('it is a List!');
      break;
    default:
      print('Unmanaged type $type');
      break;
  }
}

this program has the following output:

Unmanaged type List<Map> Unmanaged type _InternalLinkedHashMap<String, int> Unmanaged type List

The first case cannot be checked in a switch statement because if I try to set a "List<Map>" case I get the error: in constant expressions, operand(s) of this operator must be of type 'num' The second cannot be matched because using the _InternalLinkedHashMap in a case gets the following error: Case expression must be constant.

In the last case I have defined the List as a List of ints (List<int>), but the system ignores it and considers it as a simple List. I think that this is misleading and such a declaration should be forbbiden.

Any help/suggestion?

like image 752
J F Avatar asked Feb 07 '23 13:02

J F


1 Answers

If you want to do flow control based on object's type, you actually want to do it based on whether an object's class implements an interface, not what it's run time type is. This is what the Type Test Operators is and is! are for.

Remember that in Dart a class is also an interface, so you can test if an object is a specific class.

class Something {
 ...
}

var s = new Something();
print(s is Something);     // true

Note that things we tend to think of as 'classes' such as List an Map, are not classes, they are interfaces. Anything that returns a instance of such (including constructors) in fact returns a class that implements the interface.

You can use generics, but be careful.

void main() {

  var a = [1, 2, 'three'];
  print(a is List);          // True
  print(a is List<int>);     // True!!!!
  print(a is List<String>);  // True!!!!

  var b = new List<int>.from([1, 2, 3]);
  print(b is List);          // True
  print(b is List<int>);     // True
  print(b is List<String>);  // False
}

A class can implement an interface via explicitly implementing it, inheriting from a class that implements it, or through a mix-in.

class Base {
  void a() {}
}

class Mix {
  void b() {}
}

class Mixed extends Base with Mix {} 

class Explicit implements Base {
  void a() {}  
}

void main() {

  var c = new Mixed();
  print(c is Mixed);         // True
  print(c is Base);          // True
  print(c is Mix);           // True

  var d = new Explicit();
  print(d is Base);          // True
}
like image 123
Argenti Apparatus Avatar answered Feb 17 '23 01:02

Argenti Apparatus