I've got a nicely setup set of generic functions for my database crud actions. I need a little more fine grained control for a few specialized functions. I want to be able to search through list of database objects by property. Seems impossible, with one caveat- the fact that all objects will have a property of uuid, which is what I want to search by. Sooo... it must be possible with some genius minds from SO.
Of course, I want to do something like this:
Future<int> getExampleIndexByUUID({required String uuid}) async
=> await Hive.openBox<Example>('Example_Box')
.then((box) => box.values.toList().indexWhere(example)
=> example.uuid == uuid);
But this above is not possible for generic types:
Future<T> getExampleIndexByUUID<T>({
required T objectType,
required String uuid,
}) async => await Hive.openBox<T>(objectDatabaseNameGetter(objectType))
.then((box) => box.values.toList().indexWhere(example)
=> example... ); // Dead end- no property access here
PS I am aware that I can create methods outside the generic function to handle this. I can also create yet another large switch case to handle this, but this is what I want to avoid. I want to learn to abstract my code better in such a scenario. Any help or pointers appreciated! If my only option is to have a switch case or do the work outside the function, so be it.
CodePudding user response:
Define a common interface.
The best approach would be to make all of your classes that have a
uuid
property share a common base class, and then your generic function could restrict its type parameter to subtypes of that class:abstract class HasUuid { String get uuid; } class Example implements HasUuid { @override String uuid; Example(this.uuid); } Future<int> getExampleIndexByUUID<T extends HasUuid>({ required T objectType, required String uuid, }) async { var box = await Hive.openBox<T>(objectDatabaseNameGetter(objectType)); return box.values.toList().indexWhere( (example) => example.uuid == uuid), ); }
Use callbacks.
If you don't control the classes you want to use, you could have your generic function accept a callback instead to retrieve the desired property. This would be more work for callers, but it also would be more flexible since callers can choose which property to access.
Future<int> getExampleIndexByUUID<T>({ required T objectType, required String Function(T) getUuid, required String uuid, }) async { var box = await Hive.openBox<T>(objectDatabaseNameGetter(objectType)); return box.values.toList().indexWhere( (example) => getUuid(example) == uuid), ); }
You could generalize that further:
Future<int> getExampleIndex<T, PropertyType>({ required T objectType, required PropertyType Function(T) getProperty, required PropertyType propertyValue, }) async { var box = await Hive.openBox<T>(objectDatabaseNameGetter(objectType)); return box.values.toList().indexWhere( (example) => getProperty(example) == propertyValue), ); }
Use duck-typing.
If you can guarantee that all supplied types have a
uuid
member, another alternative would be to usedynamic
and duck-typing (forgoing static type-safety):Future<int> getExampleIndexByUUID<T>({ required T objectType, required String Function(T) getUuid, required String uuid, }) async { var box = await Hive.openBox<T>(objectDatabaseNameGetter(objectType)); return box.values.toList().indexWhere( (dynamic example) => example.uuid == uuid), ); }
As an aside, it's bad style to mix async
/await
with Future.then
. Just use async
/await
.