25

I'm trying to create an abstract class Firestorable which will ensure that subclasses override a named constructor fromMap(Map<String, dynamic> map)

The code looks like this ...

abstract class Firestorable {
  /// Concrete implementations will convert their state into a
  /// Firestore safe [Map<String, dynamic>] representation.
  Map<String, dynamic> toMap();

  /// Concrete implementations will initialize its state
  /// from a [Map] provided by Firestore.
  Firestorable.fromMap(Map<String, dynamic> map);
}

class WeaponRange implements Firestorable {
  int effectiveRange;
  int maximumRange;

  WeaponRange({this.effectiveRange, this.maximumRange});

  @override
  WeaponRange.fromMap(Map<String, dynamic> map) {
    effectiveRange = map['effectiveRange'] ?? 5;
    maximumRange = map['maximumRange'] ?? effectiveRange;
  }

  @override
  Map<String, int> toMap() {
    return {
      'effectiveRange': effectiveRange,
      'maximumRange': maximumRange ?? effectiveRange,
    };
  }
}

I don't get any errors when I do this, however I also don't get a compile error when I leave out the concrete implementation of the fromMap(..) constructor.

For example the following code will compile without any errors:

abstract class Firestorable {
  /// Concrete implementations will conver thier state into a
  /// Firestore safe [Map<String, dynamic>] representation.
  Map<String, dynamic> convertToMap();

  /// Concrete implementations will initialize its state
  /// from a [Map] provided by Firestore.
  Firestorable.fromMap(Map<String, dynamic> map);
}

class WeaponRange implements Firestorable {
  int effectiveRange;
  int maximumRange;

  WeaponRange({this.effectiveRange, this.maximumRange});

//   @override
//   WeaponRange.fromMap(Map<String, dynamic> map) {
//     effectiveRange = map['effectiveRange'] ?? 5;
//     maximumRange = map['maximumRange'] ?? effectiveRange;
//   }

  @override
  Map<String, int> convertToMap() {
    return {
      'effectiveRange': effectiveRange,
      'maximumRange': maximumRange ?? effectiveRange,
    };
  }
}

Am I not able to define an abstract named constructor and have it be a require implementation in a concrete class? If not, what would be the correct way to do this?

ra9r
  • 4,528
  • 4
  • 42
  • 52
  • 2
    Also see https://stackoverflow.com/questions/56484370/how-to-declare-factory-constructor-in-abstract-classes – jamesdlin Jun 07 '19 at 07:33
  • I am as well working on a "Firestorable" implementation to make it easier to access firebase. Similar to you I want to enforce the programmer to implement a toMap and fromMap method. Do you mind sharing how you have implemented this? Is there a solution? I tried interfacing with an abstract static method, however this is not supported by dart as well, therefore, I am hanging dry. Let me know :) – Jens Mar 04 '20 at 16:11
  • @Jens I ended up going in a different direction and created a generator using dart annotations. It doesn't have the same enforcement but provided me with a more complete solution that generates the serialization methods for me based on properties/annotations in the class. – ra9r Mar 06 '20 at 04:56
  • Ah I see - awesome idea. In the long run i also wanted to implement a plugin which enables me to generate code for model and entity classes. Will keep the annotations in mind! – Jens Mar 06 '20 at 07:50

1 Answers1

10

As stated in the official guide for the Dart Language constructors aren’t inherited so you can't enforce a factory constructor to subclasses. To ensure an implementation it should be part of the class' interface which constructors are not. You can check these related stackoverflow questions for more information:

How to declare factory constructor in abstract classes

How do i guarentee a certain named constructor in dart

Ali Türkay Avci
  • 1,038
  • 12
  • 12