Reputation: 1020
I am trying to create a generic Dart method that takes a generic type T where T has to implement class IModel. However,
It seems like I can't set an implements
constraint on type T, only an extends
. For example <T implements IModel
does not work but <T extends IModel>
works.
IModel.dart which serves as an interface:
class IModel {
IModel();
factory IModel.fromJson(Map<String, dynamic> json) {
return IModel();
}
Map<String, dynamic> toJson() => {};
IModel clone() {
return IModel();
}
}
MyModel.dart, an example that implements IModel:
import 'dart:core';
import 'package:.../helpers/DateTimeHelper.dart';
import 'package:.../helpers/StringHelper.dart';
import 'package:.../models/IModel.dart';
class MyModel implements IModel {
String id;
String name;
String companyId;
String projectId;
String description;
DateTime created;
DateTime updated;
MyModel({this.id, this.name, this.description, this.created, this.updated, this.companyId, this.projectId});
factory MyModel.fromJson(Map<String, dynamic> json) {
return MyModel(id: json["id"], name: json["name"], created: DateTime.parse(json["created"]), updated: DateTime.parse(json["updated"]), description: json["description"], companyId: json["companyId"], projectId: json["projectId"]);
}
Map<String, dynamic> toJson() => {
"Id": id,
"Name": name,
"Description": description,
"CompanyId": companyId,
"ProjectId": projectId,
"Created": created.toIso8601String(),
"Updated": updated.toIso8601String()
};
MyModel clone() {
return new MyModel(id: StringHelper.clone(id), companyId: StringHelper.clone(companyId), created: DateTimeHelper.clone(created), updated: DateTimeHelper.clone(updated), name: StringHelper.clone(name), projectId: StringHelper.clone(projectId), description: StringHelper.clone(description));
}
}
ModelHelper.dart with the generic method:
import 'dart:convert';
import 'package:.../models/IModel.dart';
// 'T implements IModel' not working, but 'T extends IModel' does as constraint.
abstract class ModelHelper {
static List<T> listFromJson<T implements IModel>(String json) {
List l = jsonDecode(json)["data"];
var models = l.map((m) => T.fromJson(m)).toList();
return models;
}
}
Are implements
constraints not supported in Dart? Do all T classes simply have to extend IModel?
Upvotes: 6
Views: 3425
Reputation: 89956
There is no way to specify an implements
constraint for a generic type argument, and such a constraint should never be necessary. Given:
class Base {}
class Derived1 extends Base {}
class Derived2 implements Base {}
void genericFunction<T extends Base>() {}
the generic type constraint T extends Base
means that the type argument derives from Base
. It does not (and should not) require that the type argument uses the extends
keyword. Whether the type argument uses extends
or implements
is an implementation detail of that class (Derived1
or Derived2
in this example) and should be of no concern to the generic (genericFunction
) itself. Instances of Derived1
and of Derived2
both provide the same interface as Base
regardless of whether they use extends
or implements
, and that's all that matters.
Even if there were an implements
constraint for generic type arguments, it wouldn't do what you want. You have the misunderstanding that Derived implements Base
will provide the same constructors or static
methods as Base
. In Dart, constructors and static
methods are not part of the class's interface. They are not inheritable. For example:
class Base {
Base.namedConstructor();
static void staticMethod() {}
}
class Derived implements Base {} // Perfectly legal.
void main() {
var d = Derived(); // Legal.
d = Derived.namedConstructor(); // ERROR
Derived.staticMethod(); // ERROR
}
There is no way for a generic to require that a type argument have a particular constructor or static
method and therefore there is no way to invoke constructors or static
methods on type arguments. In general, you instead would need to pass a callback that invokes the desired constructor or static
method. Specifically for serializing/deserializing JSON, you should use an existing package that utilizes code-generation (such as package:json_serializable
or package:built_value
) instead of trying to implement it yourself.
Also see:
Upvotes: 0
Reputation: 276997
Problem
It seems like I can't set an implements constraint on type T, only an extends. For example <T implements IModel does not work but works.
This is not a problem. You can safely use <T extends IModel>
, even for classes that implements IModel
:
abstract class Abstract {}
class Concrete implements Abstract {}
void function<T extends Abstract>(T value) {
print(value);
}
void main() {
function<Concrete>(Concrete()); // works
}
Upvotes: 11