Many methods like complete in class Completer are marked "abstract", but in fact It can be directly invoked without being implemented. I'm really confused. Could anyone help me?
Yes, this can be a bit confusing. While abstract classes cannot be instantiated, it is possible to make them appear to be instantiable by defining a factory constructor. This is what Completer, Future and other abstract classes do:
abstract class Completer<T> {
factory Completer() => new _CompleterImpl<T>();
...
}
You can then invoke methods on the object created by the factory constructor. In the example above, factory Completer() returns a new _CompleterImpl object. Look at the (truncated) code of that class:
class _CompleterImpl<T> implements Completer<T> {
final _FutureImpl<T> _futureImpl;
_CompleterImpl() : _futureImpl = new _FutureImpl() {}
Future<T> get future {
return _futureImpl;
}
void complete(T value) {
_futureImpl._setValue(value);
}
...
}
and you see complete(); that is the method being invoked.
Related
I have such an example of a model:
class BirthdayModel {
List birthdays;
BirthdayModel({
#required this.birthdays,
});
factory BirthdayModel.fromJson(json){
return BirthdayModel(birthdays: json['data']);
}
Map<String, dynamic> toJson() {
return {
'birthdays': birthdays,
};
}
}
I want to transfer multiple models into one method:
exampleMethod(model: BirthdayModel);
and then in this method call the constructors or methods of the passed class
exampleMethod(#required model){
return model.fromJson(data);
}
Is it possible to do this?
Not the way you write it.
You cannot pass a class as argument. Even type arguments only pass types, so you cannot use static members that way.
What you can do is:
T exampleMethod<T>(T createModelFromJson(dynamic json)){
return createModelFromJson(data);
}
and call it as :
var birthday = exampleMethod(BirthdayModel.fromJson);
There is no way to access the fromJson programmatically - it's not an instance method so there is no interface for it. Static methods must be accessed explicitly.
(I'm ignoring dart:mirrors because you probably won't have access to those).
I have an abstract Dart class which contains some abstract methods and a concrete, implemented method that I want to keep consistent across all subclasses.
IntelliJ's Dart Analysis system is reporting in subclasses that no concrete implementation exists for my implemented method — even though I can see that abstract has a concrete implementation.
I have also tried implementing the method in my subclass and calling the super class, but that is also reported as not being implemented. Both abstract class and child class are in the same file.
abstract class Exporter {
List<String> getHeaderRow();
List<dynamic> getEntryAsRow(EntriesData entry);
/// Concrete implementation
dynamic getVal(dynamic value) {
return null;
}
}
// ExpenseReport underlined in red: missing concrete implementation of getVal
class ExpenseReport implements Exporter {
List<String> getHeaderRow() {
return [];
}
List<dynamic> getEntryAsRow(EntriesData entry) {
return [];
}
// dynamic getVal(dynamic value) {
// super.getVal(value); // IntelliJ reports "getval" as abstract in superclass
// }
}
Saying
class ExpenseReport implements Exporter
you mean your ExpenseReport implements interface of Exporter, which in turn means that it has to implement all methods declared in Exporter.
If you want to inherit implemented methods from abstract Exporter you need to extend it like this:
class ExpenseReport extends Exporter
I was trying to write an abstract JsonSerializable class in order to have something like this:
abstract class JsonSerializable<T>{
T fromMap(Map<String,dynamic> map);
Map<String,dynamic> toMap();
T fromJsonString(String jsonString){
return fromMap(json.decode(jsonString));
}
String toJsonString(){
return json.encode(toMap());
}
}
My first problem was that fromMap and fromJsonString cannot be used as constructors, so I figured that could do something like this:
class Foo extends JsonSerializable<Foo>{
Foo();
factory Foo.fromMap(Map<String,dynamic> map){
return fromMap(map);
}
#override
Foo fromMap(Map<String, dynamic> map) {
// TODO: implement fromMap
return null;
}
#override
Map<String, dynamic> toMap() {
// TODO: implement toMap
return null;
}
}
Now, the code above causes the Dart analyzer to complain since I'm trying to access an instance method form a factory constructor.
So I thought I could just implement those methods without inheriting and overriding them (I know, it's a dirty solution), but still this won't provide me something that could just tell "hey this kind of class has this method".
Am I completely missing the point in this?
Is there a clean solution which does not involve code generation plugins, which apparently do not support generics?
My API responses have all this structure:
{
"key1":"value1",
"otherStuffKey":["blah", "blah"],
"data": <the object I want to generalize>
}
I need generics because I'd like to parse the object serialized as data value into different classes which have the same behavior.
PS: The toMap and toJsonString methods are useless if I'm going to parse the response, but they would be pretty useful to complete the circle and allow the use of this JsonSerializable for serializing requests too.
I have an abstract superclass with a factory that returns an instance of a subclass. Is it possible to have a method that is implemented only in superclass? In the following code, for instance, would it be possible to remove Wind::act()?
abstract class Element {
final String action; // what it does
String act() => action; // do it
factory Element() {
return new Wind();
}
}
class Wind implements Element {
final action = "blows";
act() => action; // Why is this necessary?
}
void main() {
print(new Element().act());
}
When removing Wind::act(), there is an error about it missing. Also, when extending rather than implementing the superclass, leaving out the subclass implementation doesn't cause an error. But with a factory method, extending is not an option.
To inherit functionality from Element in Wind, you need to either extend or mix-in Element in Wind. Merely implementing an interface will not inherit any implementation.
So, you need to have class Wind extends Element { ... }.
That's not currently possible because Element has no generative constructor that Wind can use as super-constructor. So, you need to add that too, and make sure to initialize the action field in that constructor.
class Element {
final String action;
Element._(this.action); // Generative constructor that Wind can use.
factory Element() = Wind; // Factory constructor creating a Wind.
String act() => action;
}
class Wind extends Element {
Wind() : super._("blows");
}
The generative constructor doesn't need to be private, but if you are declaring and using all the classes only inside your own library, it might as well be.
Another option is to have a separate ElementBase class containing the action field and act function and an empty-named generative constructor. Mixins are not a good choice in this case, because there is no good way to make action final when mixins can't have constructors.
abstract class Element {
String get action;
factory Element() = Wind;
String act();
}
class ElementBase implements Element {
final String action;
ElementBase(this.action);
String act() => action;
}
class Wind extends ElementBase {
Wind() : super("blow");
}
It's a common problem to want both a generative constructor for subclasses and a factory constructor generating the default implementation in an interface/skeleton class. The List and Map interfaces have this problem, and have solved it by exposing ListBase and MapBase. I think that is the best solution when you are exposing the superclass to other users in other libraries. If it's only used internally by yourself, I'll use the private/non-default-named generative constructor in the superclass.
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.