I have a class B extends class A, and it have the toJson method override the class A's toJson method. But when i force class B to class A, the toJson method not be changed from class B to class A.
class A {
String str1;
A(this.str1);
Map<String, dynamic> toMap() => {
'str1': str1,
}
}
class B extends A {
String str2;
B(this.str2, str1) : super(str1);
Map<String, dynamic> toMap() => {
'str2': str2,
'str1': str1,
}
}
void test(A a) {
print(a.toMap());
}
// When I pass class B in class A parameter, it still use the class B.toMap method.
B b = B('str2', 'str1');
test(b);
All Dart instance methods are virtual, also known has having dynamic dispatch. That means that when you call a.toMap(), which toMap implementation gets called depends on the runtime type of the object a, not the static type of the expression a.
So, in this case, you are calling toMap on an instance of B, so you use B's toMap.
This is a fairly common behavior in object oriented languages (dynamic dispatch is one of the hallmark features of object orientation: The object determines how to respond to the message you send it, here the request to do a "toMap" operation.)
Some object oriented languages also have non-virtual methods. Dart does not.
You can either define a static method:
class A {
static Map<String, dynamic> toMap(A value) => ...;
}
class B {
static Map<String, dynamic> toMap(B value) => ...;
}
and then choose which one to call:
print(A.toMap(a));
or you can use extension methods (which are really just static methods with a nicer call syntax) because they dispatch based on the static type of the receiver:
extension AToMap on A {
Map<String, dynamic> toMap() => ...;
}
extension BToMap on B {
Map<String, dynamic> toMap() => ...;
}
and call it normally:
print(a.toMap()); // Chooses AToMap.toMap based on static type of `a`.
In most situations, if you really want to access both A.toMap and B.toMap on an instance of B, you're better of renaming one of the methods to avoid the naming conflict.
Related
I want to set up a mixin with standard persistence behavior. The mixin looks like that:
mixin Persistence<T> {
static late final PersistenceBase persistence;
static void add(T object) {
persistence.add(object);
}
static void delete(T object) {
persistence.delete(object);
}
static T elementAt(int index) => persistence.elementAt(index);
static Iterable<T> getAll() => persistence.getAll();
static T get first => persistence.getFirst();
static T get last => persistence.getLast();
static int get length => persistence.getLength();
}
Usage of persistence would look like that:
class A with Persistence {
String var1;
int var2;
}
A.add(A("test", 1));
var length = A.getLength();
A firstElement = A.getFirst();
However Dart doesn't allow using generics with static methods:
Static members can't reference type parameters of the class.
Try removing the reference to the type parameter, or making the member an instance member
The point is that I don't want to create instance of A class just to access persistence functionality for the class.
So far I have done this way:
class PersistenceManager<T> {
late final PersistenceBase persistence;
void add(T object) {
persistence.add(object);
}
void delete(T object) {
persistence.delete(object);
}
T elementAt(int index) => persistence.elementAt(index);
Iterable<T> getAll() => persistence.getAll();
T get first => persistence.getFirst();
T get last => persistence.getLast();
int get length => persistence.getLength();
}
class Asset {
static final persistenceMgr = PersistenceManager<Asset>();
...
}
Asset.persistenceMgr.add(Asset());
That though requires adding mandatory static field for each class that would use persistence.
Is there other more elegant solution?
This is not going to work.
Dart mixin application works by mixing in the instance members of the mixin, and doing nothing to the static members.
You won't be able to do A.elementAt(...) with or without generics, because the A class doesn't have any static elementAt method. The Persistance mixin has one, but you have to call it as Persistance.elementAt. It's not the same namespace as A, and there is no inheritance, or mixin-in, of static members.
If you want a PersistanceBase per class, you do need to declare it yourself, and putting the methods which operate on the base onto the same per-class value seems like the optimal design.
So, no, there is no other more elegant solution. What you have is elegant.
I'm trying to have a base Freezed interface which my app entity interfaces can extend so I can call the freezed functions on the interfaces. I've started the process here which seems to be working so far:
abstract class IUserRegistrationEntity<T> extends FreezedClass<T> {
String get nickName;
String get email;
String get confirmEmail;
String get password;
String get confirmPassword;
}
abstract class FreezedClass<T> {
T get copyWith;
Map<String, dynamic> toJson();
}
freezed class:
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:vepo/domain/user_registration/i_user_registration_entity.dart';
part 'user_registration_entity.freezed.dart';
part 'user_registration_entity.g.dart';
#freezed
abstract class UserRegistrationEntity with _$UserRegistrationEntity {
#Implements.fromString(
'IUserRegistrationEntity<\$UserRegistrationEntityCopyWith<IUserRegistrationEntity>>')
const factory UserRegistrationEntity(
{String nickName,
String email,
String confirmEmail,
String password,
String confirmPassword}) = _IUserRegistrationEntity;
factory UserRegistrationEntity.fromJson(Map<String, dynamic> json) =>
_$UserRegistrationEntityFromJson(json);
}
But now I need to add the fromJson factory constructor to the interface. I think this may be what I'm looking for although I can't really tell how to implement it in my code:
T deserialize<T extends JsonSerializable>(
String json,
T factory(Map<String, dynamic> data),
) {
return factory(jsonDecode(json) as Map<String, dynamic>);
}
You an then call it with:
var myValue = deserialize(jsonString, (x) => MyClass.fromJson(x));
Any help adding the fromJson to my freezed interface would be appreciated.
I've found a way to get the same benefits of programming to an interface or "abstraction" with freezed objects, while still getting to call those freezed functions:
#freezed
abstract class Person with _$Person {
#With(BasicPersonMixin)
const factory Person.basicPerson(
{int? id, String? firstName, String? lastName}) = BasicPerson;
#With(FancyPersonMixin)
const factory Person.fancyPerson({String? firstName, required String extraPropMiddleName, String? lastName}) =
FancyPerson;
factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json);
const Person._();
void functionThatEveryPersonShares() {
print('I am a person');
}
String greet() {
return 'override me with a mixin or abstract class';
}
}
mixin FancyPersonMixin {
String get extraPropMiddleName {
return 'my default middle name is John`;
}
String greet() {
return 'Salutations!';
}
void specialisedFunctionThatOnlyIHave() {
print('My middle name is $extraPropMiddleName');
}
}
mixin BasicPersonMixin {
String greet() {
return 'Hi.';
}
}
Now we have 2 concrete classes: BasicPerson, and FancyPerson which are both a Person. Now we can program to Person throughout the app, and still call .copyWith and .fromJson and so on and so forth. The different types of Person can vary independently from each other by using mixins and still be used as a Person type. Works with generics etc (from docs - #With.fromString('AdministrativeArea<House>')) but I have kept the example simple for this question to most simply show the benefits. You can also make Person extend another base class.
I've found another way to let you be a bit more abstract than my other answer. Say you're in a highly abstract super-class, so you don't want to work with objects as specific as Person. You want to work with "a base freezed object"; just cast your type to dynamic in brackets and go ahead and use copyWith freely. Sure, it's not typesafe, but it's a worthy option if it allows you to do something in a super-class rather than in every sub-class.
mixin LocalSaveMixin<TEntity extends LocalSaveMixin<TEntity>> on Entity {
LocalRepository<TEntity> get $repository;
Ref? get provider;
TEntity $localFetch() {
return ($repository.$localFetch() as dynamic).copyWith(provider: provider)
as TEntity;
}
TEntity $localSave() {
return $repository.$localSave(entity: this as TEntity);
}
}
I am attempting to construct an abstract class that requires a named constructor in Dart. Given some Map (m), this generic type must be able instantiate itself.
The Dart compiler is throwing T.fromJson -> Invalid constructor name.
My attempt at coding this:
abstract class JsonMap<T> {
Map toJson();
T.fromJson(Map m);
}
I struggled with the same concept (in the same place ... API parsing :)) ) and I didn't found a proper solution.
But maybe you can use something this thing I found while checking block pattern this (I am not using it for my model part):
abstract class SomeBase {
void load();
}
class Provider<T extends SomeBase> extends InheritedWidget {
final T something;
Provider({
Key key,
#required this.something,
}): super(key: key);
#override
bool updateShouldNotify(_) {
return true;
}
static Type _typeOf<T>() => T;
static T of<T extends SomeBase>(BuildContext context){
final type = _typeOf<Provider<T>>();
Provider<T> provider = context.inheritFromWidgetOfExactType(type);
return provider.something;
}
}
OR just use this without encapsulating it in an inherited widget and provide the already initialised objects (like user or whatever you are parsing) that just load the values from the JSON provided.
You're creating a class named JsonMap that is parameterized on type T. T is not the name of your class, so T.fromJson is not a valid named constructor for JsonMap.
If you want JsonMap to have a named constructor, it should be JsonMap.fromJson(Map m).
Untested, but off the top of my head, you should write your code like so:
abstract class JsonMap<T> {
Map<String, dynamic> toJson();
T fromJson(Map<String, dynamic> m);
}
The dot makes fromJson(Map m) a constructor of type T, or a static function belonging to type T. Without the dot, it is a function belonging to the abstract class JsonMap, returning type T. Specifying the map type is good practice if you know what it will be (like with json).
i have no experience with extended classes
so don't be shocked... that's what I got:
the 'basic class' I want to extend in my models
to avoid repeat fromJson/toJson every 2 lines
import 'dart:convert';
class BaseModel {
Map<String, dynamic> json2Map(String json) => jsonDecode(json);
String map2Json(Map<String, dynamic> map) => jsonEncode(map);
json2List(String jsonList) {
List<Map<String, dynamic>> _list = [];
jsonDecode(jsonList).forEach((_json) => _list.add(jsonDecode(_json)));
return _list;
}
mapList2Json(List<Map<String,dynamic>> list) {
List<String> jsonList= [];
list.forEach((_map) => jsonList.add(map2Json(_map)));
return jsonEncode(jsonList);
}
}
and here is one of the class that extends this:
import 'package:bloc_hub/models/base_model.dart';
class Info extends BaseModel {
final String name;
final String company;
Info({this.name,this.company});
factory Info.fromMap(Map<String, dynamic> json) => new Info(
name: json['name'],
company: json['company'],
);
Map<String, dynamic> toMap() {
var map = new Map<String, dynamic>();
map['name'] = name;
map['company'] = company;
return map;
}
}
(I'm in a streambuilder and client.info is a json)
then... when I try to call 'json2map'
which is from the extended class...
Info info = Info.fromMap(json2Map(client.info));
i get this:
[dart] The method 'json2Map' isn't defined for the class 'ListPage'. [undefined_method]
what did I get wrong?
if I wasn't clear don't refrain to ask me anything
thank you for your help
[edit: bonus question
how a mixin is different from what I'm doing?]
json2Map is an instance method of BaseModel, so in order to call it you must use an instance of BaseModel or a class that extends it (like Info), like:
var b = new BaseModel();
b.json2Map(something);
The error message says you're calling it from ListPage, so the method is not found.
Alternatively, you could make the methods static and call it like BaseModel.json2Map(...) (without an instance).
There are some good explanations about mixins here (with Python examples, but the concepts are the same for Dart). I guess in your example it would make more sense to have a mixin with JSON related functions, since you could use them in other kind of objects.
A few times now I've run into a use case where I need to define an interface for how classes construct themselves. One such example could be if I want to make an Interface Class that defines the interface by which objects can serialize and unserialize themselves (for input into a database, to be sent as JSON, etc). You might write something like this:
abstract class Serializable {
String serialize();
Serializable unserialize(String serializedString);
}
But now you have a problem, as serialize() is properly an instance method, and unserialize() should instead be a static method (which isn't inheritable or enforced by the Interface) or a constructor (which also isn't inheritable).
This leaves a state where classes that impliment the Serializable interface are required to define a serialize() method, but there is no way to require those classes to define a static unserialize() method or Foo.fromSerializedString() constructor.
If you make unserialize() an instance method, then unserializing an implementing class Foo would look like:
Foo foo = new Foo();
foo = foo.unserialize(serializedString);
which is rather cumbersome and ugly.
The only other option I can think of is to add a comment in the Serializable interface asking nicely that implementing classes define the appropriate static method or constructor, but this is obviously prone to error if a developer misses it and also hurts code completion.
So, is there a better way to do this? Is there some pattern by which you can have an interface which forces implementing classes to define a way to construct themselves, or something that gives that general effect?
You will have to use instance methods if you want the inheritance guarantees. You can do a bit nicer than manual instantiation though, by using reflection.
abstract class Serializable {
static Serializable fromSerializedString(Type type, String serializedString) {
ClassMirror cm = reflectClass(type);
InstanceMirror im = cm.newInstance(const Symbol(''), []);
var obj = im.reflectee;
obj.unserialize(serializedString);
return obj;
}
String serialize();
void unserialize(String serializedString);
}
Now if someone implements Serializable they will be forced to provide an unserialize method:
class Foo implements Serializable {
#override
String serialize() {
// TODO: implement serialize
}
#override
void unserialize(String string) {
// TODO: implement unserialize
}
}
You can get an instance like so:
var foo = Serializable.fromSerializedString(Foo, 'someSerializedString');
This might be a bit prettier and natural than the manual method, but keep in mind that it uses reflection with all the problems that can entail.
If you decide to go with a static method and a warning comment instead, it might be helpful to also provide a custom Transformer that scans through all classes implementing Serializable and warn the user or stops the build if any don't have a corresponding static unserialize method or constructor (similar to how Polymer does things). This obviously wouldn't provide the instant feedback the an editor could with instance methods, but would be more visible than a simple comment in the docs.
I think this example is a more Dart-like way to implement the encoding and decoding. In practice I don't think "enforcing" the decode signature will actually help catch bugs, or improve code quality. If you need to make the decoder types pluggable then you can make the decoders map configurable.
const Map<String,Function> _decoders = const {
'foo': Foo.decode,
'bar': Bar.decode
};
Object decode(String s) {
var obj = JSON.decode(s);
var decoder = _decoders[obj['type']];
return decoder(s);
}
abstract class Encodable {
abstract String encode();
}
class Foo implements Encodable {
encode() { .. }
static Foo decode(String s) { .. }
}
class Bar implements Encodable {
encode() { .. }
static Foo decode(String s) { .. }
}
main() {
var foo = decode('{"type": "foo", "i": 42}');
var bar = decode('{"type": "bar", "k": 43}');
}
A possible pattern I've come up with is to create a Factory class that utilize instance methods in a slightly less awkward way. Something like follows:
typedef Constructable ConstructorFunction();
abstract class Constructable {
ConstructorFunction constructor;
}
abstract class Serializable {
String serialize();
Serializable unserialize(String serializedString);
}
abstract class SerializableModel implements Serializable, Constructable {
}
abstract class ModelFactory extends Model {
factory ModelFactory(ConstructorFunction constructor) {
return constructor();
}
factory ModelFactory.fromSerializedString(ConstructorFunction constructor, String serializedString) {
Serializable object = constructor();
return object.unserialize(serializedString);
}
}
and finally a concrete implementation:
class Foo extends SerializableModel {
//required by Constructable interface
ConstructorFunction constructor = () => new Foo();
//required by Serializable interface
String serialize() => "I'm a serialized string!";
Foo unserialize(String serializedString) {
Foo foo = new Foo();
//do unserialization work here to populate foo
return foo;
};
}
and now Foo (or anything that extends SerializableModel can be constructed with
Foo foo = new ModelFactory.fromSerializedString(Foo.constructor, serializedString);
The result of all this is that it enforces that every concrete class has a method which can create a new instance of itself from a serialized string, and there is also a common interface which allows that method to be called from a static context. It's still creating an extra object whose whole purpose is to switch from static to instance context, and then is thrown away, and there is a lot of other overhead as well, but at least all that ugliness is hidden from the user. Still, I'm not yet convinced that this is at all the best way to achieve this.
I suggest you define the unserialize function as named constructor like so:
abstract class Serializable<T> {
String serialize();
Serializable.unserialize(String serializedString);
}
This eliminates the need of static methods.
A possible implementation could look like this:
import 'dart:convert';
class JsonMap implements Serializable<JsonMap> {
Map map = {};
JsonMap() {
}
String serialize() {
return JSON.encode(map);
}
JsonMap.unserialize(String serializedString) {
this.map = JSON.decode(serializedString);
}
}
You can (de)serialize like so:
JsonMap m = new JsonMap();
m.map = { 'test': 1 };
print(m.serialize());
JsonMap n = new JsonMap.unserialize('{"hello": 1}');
print(n.map);
While testing this, I noticed that Dart will not throw any errors at you if you dont actually implement the methods that your class promises to implement with implements. This might just be a hicc-up with my local Dart, though.