What is the use of callable classes in dart lang? Following is the example code available on official dart site.
class WannabeFunction {
call(String a, String b, String c) => '$a $b $c!';
}
main() {
var wf = new WannabeFunction();
var out = wf("Hi","there,","gang");
print('$out');
}
How useful is it add a call function and call it using a class instead of creating a function itself in class
This can be useful to make "named functions":
class _Foo {
const _Foo();
void call(int bar) {}
void named() {}
}
const foo = _Foo();
Which allows both:
foo(42);
and
foo.named();
Related
For instance, in Javascript I can do something like:
class Foo {
x = 'baz';
bar() {
const someVar = 'x';
console.log(this[someVar]);
// Output: 'baz';
}
}
Hopefully that's relatively clear - it boils down to accessing a member variable by another variable's contents. How is this achieved in Dart?
This is not trivial in Dart. Dart doesn't have a syntax to access class properties with [].
There are a couple of approaches though:
Mirrors:
https://api.dartlang.org/stable/2.6.1/dart-mirrors/dart-mirrors-library.html
Basically you have access to everything and offers the biggest freedom. You can check what properties a class has, access them via names and so on. Big disadvantage is that the generated JS (if targeting web) will be huge. Flutter doesn't support it at all.
Reflectable
To deal with the large generated JS, you can use package:reflectable. Never tried it with Flutter. It's a bit more to set up and start using bit it works.
Dart only solution 1
You can overload [] operator on a class:
class Foo {
final _backing = <String, String>{
'foo': 'bar'
};
operator [](String val) {
return _backing[val];
}
}
void main() {
final inst = Foo();
print(inst['foo']);
}
Dart only solution 2
Just use a map :) Well sort of... If you are dealing with complex types and you want to add some extra functionality to your map, you can do something like this:
import 'dart:collection';
class StringMap extends Object with MapMixin<String, String> {
final _backing = <String, String>{};
#override
String operator [](Object key) {
return _backing[key];
}
#override
void operator []=(String key, String value) {
_backing[key] = value;
}
#override
void clear() {
_backing.clear();
}
#override
Iterable<String> get keys => _backing.keys;
#override
String remove(Object key) {
return _backing.remove(key);
}
}
Here's the simplest example.
class MixA{
}
class MixB{
}
class Base{
}
class MyClass extends Base with MixA, MixB{
}
main(){
var m = new MyClass();
reflect(m).superclass.mixin; //This only gives MixB. Is there a way to get a list of all the mixins?
}
I can't find a way to just get a list of all the mixins that were applied to MyClass
Each mixin application creates a new subclass:
void main(){
final m = new MyClass();
final r = reflect(m);
print(r.type.superclass.mixin);
print(r.type.superclass.superclass.mixin);
}
How to check type of Super class with Child class instance? I have below example and don't want to use dart-mirrors.
class SomeClass{
}
class SomeOtherClass extends SomeClass{
}
void main() {
var s1 = new SomeOtherClass();
someMethod(SomeClass, s1);
}
void someMethod(Type t, dynamic instance){
print(instance.runtimeType == t);
//print(instance.runtimeType is t); Does not work!
}
Update
Just today the package reflectable was released which allows to do this like with mirrors, but a transformer generates code instead to avoid using mirrors in production.
import 'package:reflectable/reflectable.dart';
// Annotate with this class to enable reflection.
class Reflector extends Reflectable {
const Reflector()
: super(typeCapability); // Request the capability to invoke methods.
}
const reflector = const Reflector();
#reflector
class SomeClass{
}
#reflector
class SomeOtherClass extends SomeClass{
}
void someMethod(Type t, dynamic instance){
InstanceMirror instanceMirror = reflector.reflect(instance);
print(instanceMirror.type.isSubclassOf(reflector.reflectType(t)));
}
void main() {
var s1 = new SomeOtherClass();
someMethod(SomeClass, s1);
}
Original
It might be directly supported when https://github.com/gbracha/metaclasses is implemented.
Currently this workaround can be used:
class IsInstanceOf<E> {
bool check(t) => t is E;
}
void someMethod(Type t, dynamic instance){
print( new IsInstanceOf<t>().check(instance));
//print(instance.runtimeType is t); Does not work!
}
This runs fine and returns the correct result but the analyzer shows a warning because t can't be used as a type.
If you wrap SomeClass in a generic class it works without a warning
class SomeClass{
}
class SomeOtherClass extends SomeClass{
}
void main() {
var s1 = new SomeOtherClass();
someMethod(new IsInstanceOf<SomeClass>(), s1);
}
void someMethod(IsInstanceOf t, dynamic instance){
print(t.check(instance));
//print(instance.runtimeType is t); Does not work!
}
class IsInstanceOf<E> {
bool check(instance) => instance is E;
}
Try it at DartPad
I was wondering if is possible to create an instance of a generic type in Dart. In other languages like Java you could work around this using reflection, but I'm not sure if this is possible in Dart.
I have this class:
class GenericController <T extends RequestHandler> {
void processRequest() {
T t = new T(); // ERROR
}
}
I tried mezonis approach with the Activator and it works. But it is an expensive approach as it uses mirrors, which requires you to use "mirrorsUsed" if you don't want to have a 2-4MB js file.
This morning I had the idea to use a generic typedef as generator and thus get rid of reflection:
You define a method type like this: (Add params if necessary)
typedef S ItemCreator<S>();
or even better:
typedef ItemCreator<S> = S Function();
Then in the class that needs to create the new instances:
class PagedListData<T>{
...
ItemCreator<T> creator;
PagedListData(ItemCreator<T> this.creator) {
}
void performMagic() {
T item = creator();
...
}
}
Then you can instantiate the PagedList like this:
PagedListData<UserListItem> users
= new PagedListData<UserListItem>(()=> new UserListItem());
You don't lose the advantage of using generic because at declaration time you need to provide the target class anyway, so defining the creator method doesn't hurt.
You can use similar code:
import "dart:mirrors";
void main() {
var controller = new GenericController<Foo>();
controller.processRequest();
}
class GenericController<T extends RequestHandler> {
void processRequest() {
//T t = new T();
T t = Activator.createInstance(T);
t.tellAboutHimself();
}
}
class Foo extends RequestHandler {
void tellAboutHimself() {
print("Hello, I am 'Foo'");
}
}
abstract class RequestHandler {
void tellAboutHimself();
}
class Activator {
static createInstance(Type type, [Symbol constructor, List
arguments, Map<Symbol, dynamic> namedArguments]) {
if (type == null) {
throw new ArgumentError("type: $type");
}
if (constructor == null) {
constructor = const Symbol("");
}
if (arguments == null) {
arguments = const [];
}
var typeMirror = reflectType(type);
if (typeMirror is ClassMirror) {
return typeMirror.newInstance(constructor, arguments,
namedArguments).reflectee;
} else {
throw new ArgumentError("Cannot create the instance of the type '$type'.");
}
}
}
I don't know if this is still useful to anyone. But I have found an easy workaround. In the function you want to initialize the type T, pass an extra argument of type T Function(). This function should return an instance of T. Now whenever you want to create object of T, call the function.
class foo<T> {
void foo(T Function() creator) {
final t = creator();
// use t
}
}
P.S. inspired by Patrick's answer
2022 answer
Just came across this problem and found out that although instantiating using T() is still not possible, you can get the constructor of an object easier with SomeClass.new in dart>=2.15.
So what you could do is:
class MyClass<T> {
final T Function() creator;
MyClass(this.creator);
T getGenericInstance() {
return creator();
}
}
and when using it:
final myClass = MyClass<SomeOtherClass>(SomeOtherClass.new)
Nothing different but looks cleaner imo.
Here's my work around for this sad limitation
class RequestHandler {
static final _constructors = {
RequestHandler: () => RequestHandler(),
RequestHandler2: () => RequestHandler2(),
};
static RequestHandler create(Type type) {
return _constructors[type]();
}
}
class RequestHandler2 extends RequestHandler {}
class GenericController<T extends RequestHandler> {
void processRequest() {
//T t = new T(); // ERROR
T t = RequestHandler.create(T);
}
}
test() {
final controller = GenericController<RequestHandler2>();
controller.processRequest();
}
Sorry but as far as I know, a type parameter cannot be used to name a constructor in an instance creation expression in Dart.
Working with FLutter
typedef S ItemCreator<S>();
mixin SharedExtension<T> {
T getSPData(ItemCreator<T> creator) async {
return creator();
}
}
Abc a = sharedObj.getSPData(()=> Abc());
P.S. inspired by Patrick
simple like that.
import 'dart:mirrors';
void main(List<String> args) {
final a = A<B>();
final b1 = a.getInstance();
final b2 = a.getInstance();
print('${b1.value}|${b1.text}|${b1.hashCode}');
print('${b2.value}|${b2.text}|${b2.hashCode}');
}
class A<T extends B> {
static int count = 0;
T getInstance() {
return reflectClass(T).newInstance(
Symbol(''),
['Text ${++count}'],
{Symbol('value'): count},
).reflectee;
}
}
class B {
final int value;
final String text;
B(this.text, {required this.value});
}
Inspired by Patrick's answer, this is the factory I ended up with.
class ServiceFactory<T> {
static final Map<Type, dynamic> _cache = <String, dynamic>{};
static T getInstance<T>(T Function() creator) {
String typeName = T.toString();
return _cache.putIfAbsent(typeName, () => creator());
}
}
Then I would use it like this.
final authClient = ServiceFactory.getInstance<AuthenticationClient>(() => AuthenticationClient());
Warning: Erik made a very good point in the comment below that the same type name can exist in multiple packages and that will cause issues. As much as I dislike to force the user to pass in a string key (that way it's the consumer's responsibility to ensuring the uniqueness of the type name), that might be the only way.
Admittedly, this is a little bit of an odd test case, but it's a problem I've ran in to. I have a class that takes a function as a parameter in it's constructor. I'd like to know if the function that was passed was called. Here's an example:
class TestClassMock extends Mock implements RealClass {
RealClass _real;
TestClassMock() {
_real = new RealClass();
when(callsTo("myNamedFunction")).alwaysCall(_real.myNamedFunction);
}
}
class RealClass {
String _name = "RealClass";
Function myNamedFunction;
RealClass() {
myNamedFunction = _theNamedFunction;
}
String _theNamedFunction() {
return _name;
}
}
class ClassThatCallsRealClass {
ClassThatCallsRealClass(Function func) {
func();
}
}
//The test
TestClassMock testClassMock = new TestClassMock();
ClassThatCallsRealClass caller = new ClassThatCallsRealClass(testClassMock.myNamedFunction);
testClassMock.getLogs(callsTo("myNamedFunction")).verify(happenedOnce);
So to explain a bit, ClassThatCallsRealClass takes a function as a parameter and calls it. If you were to pass in (Instance Of RealClass).myNamedFunction, this would in turn call the private function _theNamedFunction on RealClass. However, if you try to mock RealClass and redirect all calls from myNamedFunction to the RealClass myNamedFunction, this seems to fail. I don't see any clear way to get this to work, but I would think it'd be possible.
Any ideas?
In Dart, all functions are instances of class Function as you know since you pass an instance of Function to the ClassThatCallsRealClass constructor. Instances of Function have a method call() as shown here.
Meanwhile, Dart has a very good mocking capability described here (with thanks to #KWalrath for the update).
So all you need to do is test with mocks like with any other object. Just as described in the reference, create a spy for ClassThatCallsRealClass and a mock for your Function instance. Then use a verify(happenedOnce) on the call() method of the function.
To mock your function do this:
class MockFunction extends Mock {
call(int a, int b) => a + b;
}
var mock = new MockFunction();
mock(1,2); //returns 3
Of course the parameter list to call will match that of the real function. Pass mock to your spy on ClassThatCallsRealClass.
That worked for me:
library x;
import "package:unittest/unittest.dart";
import "package:unittest/mock.dart";
class TestClassMock extends Mock implements RealClass {
RealClass _real;
TestClassMock() {
_real = new RealClass();
when(callsTo("myNamedFunction")).alwaysCall(_real.myNamedFunction);
}
}
class RealClass {
String _name = "RealClass";
Function myNamedFunction;
RealClass() {
myNamedFunction = _theNamedFunction;
}
String _theNamedFunction() {
return _name;
}
}
class ClassThatCallsRealClass {
ClassThatCallsRealClass(Function func) {
func();
}
}
class MyFunc implements Function {
Function func;
String functionName;
MyFunc(this.func, this.functionName);
call() {
var inv = new MyInvocation(functionName);
func(inv);
}
}
main(List<String> args) {
test('xx', () {
//The test
TestClassMock testClassMock = new TestClassMock();
ClassThatCallsRealClass caller = new ClassThatCallsRealClass(new MyFunc(testClassMock.noSuchMethod, "myNamedFunction"));
testClassMock.getLogs(callsTo("myNamedFunction")).verify(happenedOnce);
});
}
class MyInvocation extends Invocation {
final String f;
MyInvocation(this.f);
bool get isGetter => false;
bool get isMethod => true;
bool get isSetter => false;
Symbol get memberName => new Symbol(f);
Map<Symbol, dynamic> get namedArguments => {};
List get positionalArguments => [];
}
testClassMock.myNamedFunction returns null so I call noSuchMethod directly instead which needs an Invocation.
Invocation is abstract so I created an implemented.
MyFunc is a class that wraps the function. MyFunc can be called as a function because it implements the call method.