Reputation: 1603
Is there a way to create a generic parseFromMap()
function that will output a correct object type?
I have multiple data classes that I deserialise from Firebase DocumentSnapshot
. I'm trying to create an abstract class that the data classes will inherit or implement. All classes should implement a parseFromMap()
function that will convert the Json into an object. Here's my attempt at the abstract class:
abstract class FirebaseConverter<T> {
FirebaseConverter(Map<String, dynamic> snapshot, String id);
Map toJson();
T parseFromMap(Map<String, dynamic> snapshot, String id);
}
Next, I want to create a generic function that will parse a DocumentSnapshot into the final object type.
Future<T> getDocumentById(String id) async {
DocumentSnapshot snap = firebaseRef.doc(id).get();
return T.parseFromMap(snap.data(), snap.id);
}
Obviously, this is failing because parseFromMap(snap, snap.id)
is not static. Abstract classes cannot have static functions.
What I've done so far is that I have a specific Future<SomeSpecificFirebaseObject> getDocumentById()
in each of the specific classes, but this is a lot of code duplication for a basic necessity.
Upvotes: 0
Views: 1562
Reputation: 724
The current getDocumentById(...)
implementation will fail, because you cannot use a Generic Type as an Instance Type. Generics are not Objects.
Here's one good method I'm referring upon: https://stackoverflow.com/a/28556110/6735934
Let's use the ItemCreator
custom function from there.
tl;dr: You basically create an empty instance of the object where you want your data to be filled on, and return it to the ItemCreator
function.
Let's begin
Consider a class FirebaseSerializer
with a similar method in the question
typedef S ItemCreator<S>();
class FirebaseSerializer {
static T serializeThis<T extends FirebaseConverter>(ItemCreator<T> itemCreator, String id) {
// (1) Casting your specific object to its base class
FirebaseConverter item = itemCreator();
// Your Firebase Specfics
DocumentSnapshot snap = firebaseRef.doc(id).get();
// Calling your base class' method, now possible
return item.parseFromMap(snap, id);
}
}
This method accepts an ItemCreator
of Type "T" which extends FirebaseConverter
, which would return an instance of T
.
Why extend? For the cast to FirebaseConverter
to work (refer (1) in above snippet)
Now, for an example:
class A implements FirebaseConverter {
@override
parseFromMap(Map<String, dynamic> snapshot, String id) {
// TODO ...
}
@override
Map toJson() {
// TODO ...
}
}
FirebaseSerializer in action!
A anInstanceOfA = FirebaseSerializer.serializeThis<A>(() => new A(), "your snap ID");
Upvotes: 2