hgl
hgl

Reputation: 2214

How to conditionally cast a type in dart?

It seems if it's just a variable, I can conditionally cast like this.

Animal animal = Dog();
if (animal is Dog) {
  animal.bark(); // animal is of type Dog here
}

But if it's a property on a class, how do I conditionally cast?

House house = House()
house.animal = Dog();
if (house.animal is Dog) {
  house.animal.bark(); // fail
}

I know I can do it like this

if (house.animal is Dog) {
  Dog animal = house.animal;
  animal.bark();
}

But that seems cumbersome. Is there anyway I can check and cast type in one go like I can with variables?

Thanks a lot.

Upvotes: 9

Views: 4650

Answers (3)

Hugo Passos
Hugo Passos

Reputation: 8447

tl;dr

Replace multiple field accesses by a local variable.

final house = House()..animal = Dog()
final animal = house.animal;

if (animal is Dog) {
  animal.bark();
} else if (animal is Cat) {
  animal.meow();
} else if (animal is Wolf) {
  animal.howl();
}

Complete answer

This is a Dart limitation. It happens because the compiler cannot guarantee that the result of the first access to animal is the same as the second access. Even is the field is final, a subclass may override its getter to change the return value at different accesses.

Check the adapted example below from Leaf Petersen. (Thanks @jamesdlin for mentioning.)

class A {
  final num x = 3;
}

class B extends A {
  num y = 3;
  num get x => y;
}

void main() {
  B b = B();
  A a = b;
  if (a.x is int) {
    b.y = 3.0;
    (a.x as int).isEven; // Fall down go boom.
  }
}

One way to work around that is by avoiding multiple field accesses. This can be done by assigning the result of the field access to a local variable such as:

final house = House()..animal = Dog()
final animal = house.animal;

if (animal is Dog) {
  animal.bark();
} else if (animal is Cat) {
  animal.meow();
} else if (animal is Wolf) {
  animal.howl();
}

Upvotes: 13

BambinoUA
BambinoUA

Reputation: 7100

Maybe it is better solution to use interfaces.

abstract class Animal {
  void doSound(); // each animal does sound
}

class Dog implements Animal {
  @override
  void doSound() {
    print('wof');
  }
}

class Cat implements Animal {
  @override
  void doSound() {
    print('meow');
  }
}

House house = House()
house.animal = Dog();
animal.doSound();
house.animal = Cat();
animal.doSound();

Upvotes: 0

Miguel de Sousa
Miguel de Sousa

Reputation: 303

You can manage a conditional type cast like the example below:

class MyException implements Exception {
  int customCode = 2;
}

void myFunc(){
  //you can switch between the values below to see the effect
  // throw Exception();
  throw MyException();
}

void main() {
  try {
    myFunc();
  } catch (e) {
    final i = e is MyException ? e.customCode : -10;
    print(i);
  }
}

Upvotes: 0

Related Questions