Context
In my code, I have an interface called AbstactDataModel
that is used as a starting point for all the data model classes. This is implemented so that i know that whatever xyzModel
class I may need, it will have a fromJson(Map<String, dynamic> json)
function that will return a new instance of the class built using the given json and a getter for the type
.
// In abstract_model.dart
abstract class AbstractDataModel {
///
/// returns a String containing the class name
/// For example, the class ColumnModel will return 'column'
///
String get type;
///
/// Will call the [.fromJson] constructor and return a new instance of the
/// object
///
dynamic fromJson(Map<String, dynamic> json);
}
And here is an example of a DataModel class that extends the AbstractDataModel
:
// in group_model.dart
class GroupModel extends AbstractDataModel {
int _id;
String _name;
// Constructors
GroupModel.empty();
GroupModel.fromJson(Map<String, dynamic> json) {
_id = parseToInt(json['id'].toString());
_name = parseToString(json['name'].toString());
}
// Getters for private fields
int get id => _id;
String get name => _name;
@override
String get type => 'group';
@override
GroupModel fromJson(Map<String, dynamic> json) => GroupModel.fromJson(json);
}
'Broken' code
I made a function that sends an API request to a server, and then would parse the json response into a DataModel type of object. In order to avoid code duplication and make the source code easier to maintain, i wanted to create a generic function, like so:
// inside the API class (which is a singleton)
Future<T> getObject<T extends AbstractDataModel>({
@required String command,
@required Map<String, dynamic> params,
}) async {
final Response response = await _sendRequest(
command: command,
params: params,
);
T object;
final Map<String, dynamic> body =
jsonDecode(response.body)['result'] as Map<String, dynamic>;
if (body == null) {
print('Request failed.');
throw const Failure('Failed request to fetch object.');
} else {
object = (T as AbstractDataModel).fromJson(body) as T;
print('Successfully fetched ${object.type}');
return object;
}
}
And the way I expected to use this, is as follows:
GroupModel group = await API().getObject<GroupModel>();
Expected behaviour
I expect the getObject
function to know that, since T
extends AbstractDataModel
, then the fromJson(...)
function exists, and use it to create a new GroupModel
object, all while only accepting objects that extend the AbstractDataModel
, i.e. throwing an error for getObject<int>()
saying something like 'int does not extend AbstractDataModel`.
Actual behaviour
During execution, I get an unhandled error that says the type _Type is not a subtype of AbstractDataModel
and as such, the cast (T as AbstractDataModel)
is invalid.
Also, setting a breakpoint and checking some values via the debugger, i tested the following statements:
T is AbstractDataModel: false
T is GroupModel: true
T is AnyOtherDataModelThatExtendsAbstractDataModel: true
So I wouldn't even be able to handle it via a switch
statement, since even if i call getObject<GroupModel>()
, the statement T is ProjectModel
for example, is still evaluated as being true. Any pointers on what i am doing wrong?