I am trying to come up with an example of how we could search for the #override metadata annotation using reflection in Dart.
In the examples I used to learn the dart:mirrors library and reflection, they were always searching for custom made annotations.
Here is an example where they search for a custom "Todo" annotation
When searching for custom made annotations, they would simply compare the metadata's reflectee to the class data type to check for a match.
In Dart's documentation linked below, you can see an example implementation of an _Override instance.
Here is Dart documentation on the override constant
This lead to me to try:
if(meta.reflectee is _Override) {
print('Found!);
}
But the "_Override" cannot be resolved and suggest no imports to access such a data instance.
I am able to toString the reflectee for comparison but I feel like it is a dirty solution:
if (meta.reflectee.toString() == "Instance of '_Override'") {
print('Found!');
}
When using the #override annotation, I am struggling to find a way to compare the metadata's reflectee to the instance type of _Override.
Here is the Dog class:
class Dog extends Animal {
Dog(String name) : super(name);
#override
void makeNoise() {
print('Bark, bark!');
}
}
Here is my reflection search code:
Dog dog = Dog('Harper');
InstanceMirror instanceMirror = reflect(dog);
ClassMirror classMirror = instanceMirror.type;
classMirror.instanceMembers.forEach((_, member) {
print(member.owner.simpleName);
print(member.simpleName);
print(member.isRegularMethod);
member.metadata.forEach((meta) {
print(meta.reflectee);
if (meta.reflectee is _Override) {
print('Found!');
}
});
});
Finally, here is my output when the instanceMembers.forEach loop gets to the method I am interested in:
Symbol("Dog")
Symbol("makeNoise")
true
Instance of '_Override'
Use:
if (meta.reflectee == override) {
print('Found!);
}
or
if (identical(meta.reflectee, override)) {
print('Found!);
}
Dart constants are canonicalized, and the override object (an instance of the private _Override class) does not override operator== from Object, so the two expressions do the same thing.
For annotation classes which have data, you don't know the exact instance, so you have to do type checks. For marker annotations like override, which only have one instance, you can compare to the exact constant instance used for annotating.
Related
So I've been playing around with code generation in Dart, and I have a problem resolving Element type in "data" classes. I have 2 data classes, and 2nd one has a type of a 1st one defined as a field.
first.dart:
part 'first.g.dart';
#Data(name="First")
class _First {
}
second.dart:
import 'first.dart';
part 'second.g.dart';
#Data(name="Second")
class _Second {
First first;
}
You might have noticed, that _Second has field First instead of _First. That's because I generate two new classes. These two new classes contain additional functionality, like generated hashCode and some other custom methods I need.
In my Generator class, I need to obtain type string of field first. I'm doing this by fieldElement.type?.getDisplayString().
This works fine for all other non-generated classes, like num for example.
But in case of first field, returned type string is "dynamic".
Is there a way to obtain correct type? Should "First" in this case.
data_generator.dart
class Data extends GeneratorForAnnotation<Data> {
#override
generateForAnnotatedElement(Element element, ConstantReader annotation,
BuildStep buildStep) {
if(element is ClassElement) {
FieldElement element = findField();
String type = element.type?.getDisplayString();
assert(type != "dynamic");
//...
}
}
//...
}
Problem I need to solve
Is there a way to get the class name of a dart class as a String or a Type object..?
class MyClass {
}
var myClass = MyClass();
I know the property, runtimeType which return the type of the object as a Type object. But is there a similar function for classes?
print(myClass.runtimeType.toString());
What I currently do is creating an object of the class and use runtimeType.
String type = MyClass().runtimeType.toString();
Note: In python there is a variable called __name__ in every class, which does what I need.
My intention
My final goal is to create dart objects using previously saved class names. In this issue they have proposed a method using Maps.
The thing is that I have lots of classes and that method looks messy in my situation.
What I currently do is, save the object type by:
var saving = myClass.runtimeType.toString();
And when loading:
if (saving == MyClass().runtimeType.toString()) {
return MyClass();
}
From your experiences and opinions, can you propose a better solution?
You can use:
var runtimeTypeName = (MyClass).toString();
or for generics:
var runtimeTypeName = T.toString();
The class type can be used as a Type:
Type myType = MyClass;
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.
I'm playing around with the mirrors stuff in Dart. I can't find any way reflect a class and figure out if it has a constructor and, if so, what the parameters to this constructor are.
With the ClassMirror, it looks like the "declarations" collection of DeclarationMirror objects will include an entry for the constructor, but there's no way with a DeclarationMirror to tell if whether or not it's a constructor, and no way to see information about the parameters.
With the "instanceMembers" collection of MethodMirror objects, it looks like constructors are not even included. I assume this is because a constructor isn't a normal kind of method that one could invoke, but still, it's odd since MethodMirror has an "isConstructor" attribute.
Is there any way to, given an object type, figure out if it has a constructor and, if so, get information on the parameters to that constructor?
The below code illustrates the problem:
import 'dart:mirrors';
class Person {
String name;
int age;
Person(this.name, this.age);
string getNameAndAge() {
return "${this.name} is ${this.age} years old";
}
}
void main() {
ClassMirror classMirror = reflectClass(Person);
// This will show me the constructor, but a DeclarationMirror doesn't tell me
// anything about the parameters.
print("+ Declarations");
classMirror.declarations.forEach((symbol, declarationMirror) {
print(MirrorSystem.getName(symbol));
});
// This doesn't show me the constructor
print("+ Members");
classMirror.instanceMembers.forEach((symbol, methodMirror) {
print(MirrorSystem.getName(symbol));
});
}
Firstly, you need to find contructors in declarations map.
ClassMirror mirror = reflectClass(Person);
List<DeclarationMirror> constructors = new List.from(
mirror.declarations.values.where((declare) {
return declare is MethodMirror && declare.isConstructor;
})
);
Then, you can cast DeclarationMirror into MethodMirror and use getter MethodMirror.parameters to get all parameters of the constructor. Something like:
constructors.forEach((construtor) {
if (constructor is MethodMirror) {
List<ParameterMirror> parameters = constructor.parameters;
}
});
I'm get null returned when attempting to access a subclass static variable through a overridden subclass accessor:
library resource;
abstract class Resource
{
String name;
String description;
Resource(this.name, this.description);
Resource.map(Map data)
{
...
_getDb()[this] = data;
}
abstract Map _getDb();
}
class Skill extends Resource
{
static Map _skills = {}
Skill.map(Map data) : super.map(data);
Map_getDb()
{
return _skills;
}
}
import 'resource.dart'
void main() {
useVMConfiguration();
test('constructor', () {
Skill skill = new Skill.map({
'name': 'foo'
});
}
}
Here I'm trying to call _getDb() on the (hopefully) now constructed subclass in the super constructor. Despite _skills being instantiated, _getDb() returns null.
Is this possible?
EDIT:
_skills is not present when inspecting this at _getDb():
this Skill [id=0]
description "bar" [id=19]
name "foo" [id=18]
Your example has several flaws as DartEditor shows.
Map_getDb() is missing a space between Map and _getDb().
Is this only in your question or in the code you run too?
abstract Map _getDb(); is also a syntax error.
In Dart a method is made abstract when you don't provide an implementation (; instead of {})
After this fixes the code works fine.