How to give child class property a different type from the superclass in Dart?

429

Interesting problem. You are trying to achieve inheritance. So, what is happening when you are providing required BrandModel brand, in the constructor of CarModel which extends Car having final Brand brand; type of field. Brand get the necessary attributes/definition from BrandModel object, and it is the only reason that your are unable to call 'brand': brand.toMap(), inside the Map<String, dynamic> toMap() method bacause Brand class does not have this method.

Child has all the attributes/functions/definition of Parent but Parent does not possess all the properties of child.

SOLUTION

So, you need to put the toMap() in the Brand class. Here Abstraction comes in. At this stage, might be the toMap() method is unclear to you like what will it have inside the body, what will it map? Simply, declare it as abstract. Also, declare the class as abstract class. abstract class should have on abstract method.

Now, Polymorphism comes in. Inside the BrandModel class, the toMap() method will be an override method. Check the following tested code (complete example).

class Car {
  final String name;
  final Brand brand;

  const Car({
    required this.name,
    required this.brand,
  });
}

abstract class Brand {
  final String label;
  final String color;

  const Brand({
    required this.label,
    required this.color,
  });
  Map<String, dynamic> toMap();
}

class CarModel extends Car {
  CarModel({
    required String name,
    required BrandModel brand,
  }) : super(
          name: name,
          brand: brand,
        );

  Map<String, dynamic> toMap() {
    return {
      'name': name,
      'brand': brand.toMap(),
    };
  }
}
class BrandModel extends Brand {
  BrandModel({
    required String label,
    required String color,
  }) : super(
          label: label,
          color: color,
        );

  @override
  Map<String, dynamic> toMap() {
    return {
      'label': label,
      'color': color,
    };
  }
}

void main() {
  BrandModel brand = BrandModel(label:'Test',color : 'white');
  
  CarModel car = CarModel(name: 'T',brand : brand);
  
  print(car.toMap());
}

Share:
429
Manu
Author by

Manu

Updated on January 01, 2023

Comments

  • Manu
    Manu over 1 year

    I am in a situation where I'm working with entities and models. Basically my entities describe how my data should look like, and my models extends those entities and have a function toMap to transform them into a json object.

    So I have 2 entities, Car and Brand, and one of the Car properties is of type Brand.

    class Car {
      final String name;
      final Brand brand;
    
      const Car({
        required this.name,
        required this.brand,
      });
    }
    
    class Brand {
      final String label;
      final String color;
    
      const Brand({
        required this.label,
        required this.color,
      });
    }
    

    Then I have 2 models CarModel and BrandModel, which extends respectively Car and Brand.

    class CarModel extends Car {
      CarModel({
        required String name,
        required BrandModel brand,
      }) : super(
              name: name,
              brand: brand,
            );
    
      Map<String, dynamic> toMap() {
        return {
          'name': name,
          'brand': brand,
        };
      }
    }
    
    class BrandModel extends Brand {
      BrandModel({
        required String label,
        required String color,
      }) : super(
              label: label,
              color: color,
            );
    
      Map<String, dynamic> toMap() {
        return {
          'label': label,
          'color': color,
        };
      }
    }
    

    As you can see, in CarModel, I would like the brand property to have a type BrandModel. However, it takes its superclass type.

    So in the end, the brand property in my CarModel has a type of Brand.

    My problem is that in the toMap function, I would like to use brand.toMap like so:

      Map<String, dynamic> toMap() {
        return {
          'name': name,
          'brand': brand.toMap(), // <= I want to use toMap() here, so I need brand to be of type BrandModel 
        };
      }
    

    Is there a way to force my child class to have a different type for a property inherited from the superclass?

  • Manu
    Manu over 2 years
    That's a very good answer, I like this approach very much! However I have another problem: the class Car and Brand are instanciated in the presentation layer of my app. If I make those classes abstract, I'm not able to use them anymore... The idea is to have the entity (Car and Brand) in my domain layer, then the data layer uses models that extends those entities and the presentation layer use the entities, so that the presentation layer does not have anything to do with the data layer
  • Faiizii Awan
    Faiizii Awan over 2 years
    if you are sure that it will be BrandModel then you can call 'brand': (brand as BrandModel).toMap(), to access the toMap method
  • Manu
    Manu over 2 years
    Ok, so this is what I ended up doing, thanks a lot!