How to conditionally cast a type in dart?
Asked Answered
B

3

9

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.

Banwell answered 28/5, 2019 at 1:41 Comment(2)
Your last example won't actually work if you disable implicit casts. It instead should use Animal animal = house.animal; if (animal is Dog) { animal.bark(); }.Bootblack
BTW, if you're curious why type promotion doesn't happen on fields, see: github.com/dart-lang/language/issues/104Bootblack
T
13

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();
}
Tentage answered 28/5, 2019 at 1:56 Comment(0)
C
0

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);
  }
}
Communalize answered 15/7, 2021 at 17:30 Comment(0)
F
0

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();

Farmhouse answered 19/4, 2022 at 14:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.