Reputation: 3391
Is there a simple, lightweight/inexpensive call to determine if an object supports a named method? So, in this the obj.respondsTo would be great.
dynamic _toJson(dynamic obj) {
return obj.respondsTo('toJson'))? obj.toJson() : obj;
}
class Foo {
String foo = "Foo.foo";
Bar bar = new Bar();
Map toJson() {
return {
"foo" : _toJson(foo),
"bar" : _toJson(bar)
};
}
}
One alternative would be just call it and catch a noSuchMethod exception, but I imagine that is bad practice and expensive?
Upvotes: 5
Views: 6527
Reputation: 2877
It's a work around, but I use a small helper which is basically inline try catch:
String json = ifHasMethodElse(() => obj.toJson(), null)
dynamic ifHasMethodElse(Function closureWithCall, dynamic elseThis) {
try {
return closureWithCall();
} catch (NoSuchMethodError, e) {
return elseThis;
}
}
Upvotes: 3
Reputation: 21651
Not lightweight, but doesn't require mirrors
(and after rereading the accepted answer, it's basically what the accepted answer suggests as a possibility):
dynamic _toJson(dynamic obj) {
try {
return obj.toJson();
} catch(NoSuchMethodError) {
return obj;
}
}
I'd used this for debugging/troubleshooting purposes only - I'd never put this code into production. Ideally, you'd be much better off defining some kind of interface and passing that in as your obj
instead of as dynamic, as @thetrompf suggests.
Upvotes: 0
Reputation: 453
Using a slightly different approach, I'm checking if the given method exists by checking the list of instance functions:
InstanceMirror im = reflect(this);
Symbol fn = new Symbol('$functionName');
if (im.type.instanceMembers.keys.contains(fn)) {
im.invoke(fn, params);
}
Upvotes: 1
Reputation: 460
You could define an interface/abstract class with an abstract method on test if the the object is of the interface type, and then call the method you now know exists.
abstract class JsonEncodable {
Map toJSON();
}
Object _toJson(Object obj) {
return (obj is JsonEncodable)? obj.toJson() : obj;
}
class Foo implements JsonEncodable {
Map toJSON() {
// toJSON implementation
}
}
Upvotes: 5
Reputation: 263009
With dart:mirrors, you should be able to write something like:
bool respondsTo(dynamic obj, String methodName)
{
var mirror = reflect(obj);
return mirror.type.methods.values.map((MethodMirror method) => method.simpleName)
.contains(methodName);
}
Upvotes: 2
Reputation: 14171
The short answer is, 'no'. The answer provided by Frédéric Hamidi is not incorrect, but it does not work in dart2js (dart:mirrors is largely unimplemented in dart2js).
Also, while checking whether an object responds to a particular method is very common in other languages (Ruby, for example), it does not seem particularly Dart-y to me. Maybe once mirrors are fully supported in Dart, this will change.
And its hard to say whether reflection based on mirrors is 'lightweight/inexpensive'. It depends on the use case and how you define these terms.
I would say that your best bet is call the method on the object, catch the NoSuchMethod exception, and implement some default error-handling behavior. This especially makes sense if you normally expect the method to be present.
Upvotes: 10