How to dynamically instantiate a class (Function.apply for class)? - dart

In Dart, I can dynamically call a function using Function.apply:
Function.apply(foo, [1,2,3], {#f: 4, #g: 5});
gives exactly the same result as
foo(1, 2, 3, f: 4, g: 5).
Question: Does a similar thing exist for instantiating classes?
Expected result would look something like:
class foo {
final String boo;
int? moo;
foo({required this.boo, this.moo})
}
...
var params = {boo: 'A string value', moo: 121};
Class.apply(foo, params);
// Gives the result:
foo(boo: 'A string value', moo: 121);

Function.apply isn't type-safe, so you should avoid using it if you can.
If you really want to use it with a constructor, you can use it with constructor tear-offs (added in Dart 2.15), which are just Functions:
class Foo {
final String boo;
int? moo;
Foo({required this.boo, this.moo});
#override
String toString() => 'Foo(boo: "$boo", moo: $moo)';
}
void main() {
var params = {#boo: 'A string value', #moo: 121};
var result = Function.apply(Foo.new, [], params);
print(result); // Prints: Foo(boo: "A string value", moo: 121)
}

As far as I know, you can make use of static methods if you want to create an instance without using another instance. Here is a sample:
class Foo {
final String boo;
final int moo;
Foo({this.boo, this.moo});
static fromValues({String boo, int moo}) {
return Foo(boo: boo, moo: moo);
}
}
void main() {
var params = {#boo: 'A string value', #moo: 121};
var fooObject = Function.apply(Foo.fromValues, [], params);
print(fooObject);
print(fooObject.boo);
print(fooObject.moo);
}
Another way is to add 'call' function to class to make it's objects callable and use an object of the class to create new objects. Here is a sample:
class Foo {
final String boo;
final int moo;
Foo({this.boo, this.moo});
call({String boo, int moo}) {
return Foo(boo: boo, moo: moo);
}
}
void main() {
Foo aFoo = Foo(boo: 'nothing', moo: 0);
var params = {#boo: 'A string value', #moo: 121};
var fooObject = Function.apply(aFoo, [], params);
print(fooObject);
print(fooObject.boo);
print(fooObject.moo);
}

Related

Dart can't access property on a generic type function parameter despite providing the type

I'm trying to specify a function parameter as a generic type T:
enum MyEnum {
Foo,
Bar
}
class DbColumn {
final Function<T>(T value) serializer;
const DbColumn({this.serializer});
}
class MyClass {
static final DbColumn rating = DbColumn(
serializer: <MyEnum>(v) {
var i = v.index;
}
);
}
However when trying to access index on v I get this type error message:
The getter 'index' isn't defined for the type 'Object'.
Try importing the library that defines 'index', correcting the name to the name of an existing getter, or defining a getter or field named 'index'.
When I hover over v in VSC it says that it's of type MyEnum.
If I instead remove the generic type and do a cast like this it works as expected:
class DbColumn {
final Function(dynamic value) serializer;
const DbColumn({this.serializer});
}
class MyClass {
static final DbColumn rating = DbColumn(
serializer: (v) {
var casted = v as MyEnum;
var i = casted.index;
}
);
}
Why is the generic type not working as expected?
EDIT:
What is even weirder is that this example works too if I put it inside MyClass:
x<T>(Function(T) c) {}
y() {
x<MyEnum>((v) {
print(v.index); // No error given and type of v is MyEnum
});
}
EDIT 2: The same problem happens when overriding methods:
abstract class MyInterface {
int someFunction<T>(T value);
}
class MyClass implements MyInterface {
#override
someFunction<MyEnum>(v) {
return v.index; // Gives same error and no intellisense happens in VSC
}
}
Instead of making the function generic, declare the class as generic and it will work as expected. Like this :
enum MyEnum {
Foo,
Bar
}
class DbColumn<T> {
final Function(T value) serializer;
const DbColumn({this.serializer});
}
class MyClass {
static final DbColumn<MyEnum> rating = DbColumn(
serializer: (v) {
var i = v.index;
print('Index : $i');
}
);
}
void main() {
MyClass.rating.serializer(MyEnum.Bar);
}
OUTPUT :
Index : 1

How to convert a GLib.Value of type GStrv (string[]) to a GLib.Variant

In the following example one class property is of type Gstrv.
With ObjectClass.list_properties() one can query the Paramspec of all properties, and with get_property() all properties can be requested as GLib.Value. How would I access the Value of type GStrv and convert it to a GLib.Variant?
My GLib version is slightly outdated, so I do not have the GLib.Value.to_variant() function available yet :( .
public class Foo: GLib.Object {
public GLib.HashTable<string, int32> bar;
public Foo() {
bar = new GLib.HashTable<string, int32>(str_hash, str_equal);
}
public string[] bar_keys { owned get { return bar.get_keys_as_array(); } }
}
int main() {
var foo = new Foo();
Type type = foo.get_type();
ObjectClass ocl = (ObjectClass) type.class_ref ();
foreach (ParamSpec spec in ocl.list_properties ()) {
print ("%s\n", spec.get_name ());
Value property_value = Value(spec.value_type);
print ("%s\n", property_value.type_name ());
foo.get_property(spec.name, ref property_value);
// next: convert GLib.Value -> GLib.Variant :(
}
foo.bar.set("baz", 42);
return 0;
}
Output:
bar-keys
GStrv
Using GLib.Value.get_boxed() seems to be working.
Example:
// compile simply with: valac valacode.vala
public class Foo: GLib.Object {
public GLib.HashTable<string, int32> bar;
public Foo() {
bar = new GLib.HashTable<string, int32>(str_hash, str_equal);
}
public string[] bar_keys { owned get { return bar.get_keys_as_array(); } }
}
public Variant first_gstrv_property_as_variant(Object obj)
{
Type class_type = obj.get_type();
ObjectClass ocl = (ObjectClass) class_type.class_ref ();
foreach (ParamSpec spec in ocl.list_properties ()) {
print ("%s\n", spec.get_name ());
Value property_value = Value(spec.value_type);
print ("%s\n", property_value.type_name ());
obj.get_property(spec.name, ref property_value);
// next: convert GLib.Value -> GLib.Variant
if(property_value.type_name () == "GStrv") {
return new GLib.Variant.strv((string[])property_value.get_boxed());
}
}
return new GLib.Variant("s", "No property of type GStrv found");
}
int main() {
var foo = new Foo();
print("%s\n", first_gstrv_property_as_variant(foo).print(true));
foo.bar.set("baz", 42);
print("%s\n", first_gstrv_property_as_variant(foo).print(true));
foo.bar.set("zot", 3);
print("%s\n", first_gstrv_property_as_variant(foo).print(true));
return 0;
}
Output:
bar-keys
GStrv
#as []
bar-keys
GStrv
['baz']
bar-keys
GStrv
['baz', 'zot']
In the generated c-code this looks as follows:
_tmp18_ = g_value_get_boxed (&property_value);
_tmp19_ = g_variant_new_strv ((gchar**) _tmp18_, -1);
Passing -1 as length to g_variant_new_strv() means the string array is considered as null terminated. Inside g_variant_new_strv() the g_strv_length() function is used to determine the length.
Hopefully it will be useful to someone else someday. :-)

Can I use generics in Dart like this?

I want to parse http response to Dart object,so I defined a abstract class
BaseBean:
abstract class BaseBean{
BaseBean.fromJson(Map<String, dynamic> json);
Map<String, dynamic> toJson();
}
And I used it in a function:
Future<ResultData<T>> netFetch<T extends BaseBean>(){
......
return new ResultData(T.fromJson(), result, code);
}
but T.fromJson() has an error:
The method 'fromJson' isn't defined for the class 'Type'
So,can I use generics in Dart like this?Is there a better way to solve this problem?
Yes, of course, it is possible, but only with a workaround:
T unmarshal<T>(Map map, {Type type}) {
if (type == null) {
type = T;
}
switch (type) {
case Order:
return Order.fromJson(map) as T;
case OrderItem:
return OrderItem.fromJson(map) as T;
case Product:
return Product.fromJson(map) as T;
default:
throw StateError('Unable to unmarshal value of type \'$type\'');
}
}
var order = unmarshal<Order>(data);
//
var product = unmarshal(data, type: Product) as Product;
//
var type = <String, Type>{};
types['OrderItem'] = OrderItem;
// ...
var type = types['OrderItem'];
var orderItem = unmarshal(data, type: type);
I used the same approach and I came to this solution.
I created a Map<Type, BaseBean Function(dynamic)> decoders.
It looks like
final decoders = {
MyClass: (data) => MyClass.fromJson(data),
};
and I get the function in this way
final MyClass myData = decoders[T]!(data);
You can also create a typedef for the BaseBean Function(dynamic) like this
typedef Decoder = BaseBean Function(dynamic);
So the Map<Type, BaseBean Function(dynamic)> will became Map<Type, Decoder>.

is there any way to spread a array in dart like we do in javaScript?

i have array that holds arguments which i want to pass to a constructor of a class
void main() {
var arr = ["abc", "bca"];
A(...arr); //something like that
}
class A {
String a;
String b;
A(this.a,this.b);
}
is there any way to do so. Please help me if you have any solution.
No, Dart doesn't support such a feature for now (Dart-2.1).
To avoid destructuring array at every call sites with A(arr[0], arr[1]) you can add an other constructor.
void main() {
var arr = ["abc", "bca"];
A.spread(arr);
}
class A {
String a;
String b;
A(this.a,this.b);
A.spread(List arr) : this(arr[0], arr[1]);
}
As there is no destructing in Dart, I simply do this:
var yourList: List<String> = ["first", "second", "third", etc];
var first = yourList.first;
var others = yourList.sublist(1);

What can i do with a stored type?

Dart allows variables of types: Type type = SomeType; But for what purpose?
For example, foo bar baz are misapplications:
class A {
Type type = List;
foo() => new type();
type bar() {
return new List();
}
type baz = new List();
}
void main() {
Type type = String;
var str = "Hello Dart";
print(type == str.runtimeType);//true
print(str is String);//true
print(str is type); //type error.
}
I think this one is pretty neat:
void main() {
foo(Type t) {
switch (t){
case int: return 5;
case List: return [1,2,3]; // This one gets me every time :(
case String: return "Hello Dart!";
default: return "default";
}}
print(foo(10.runtimeType)); //5
print(foo([2,4,6].runtimeType)); //default
print(foo("lalala".runtimeType)); //Hello Dart!
print(foo(foo.runtimeType)); //default
}
Is its sole purpose to be the return type for methods like runtimeType and type matching ?
I don't think you can use it for generics. There you need type literals. But you can use it for reflection.
Just one simple example:
import 'dart:mirrors' as mirr;
class A {
String s;
A(this.s);
#override
String toString() => s;
}
void main() {
Type type = A;
var str = "Hello Dart";
mirr.ClassMirror cm = mirr.reflectType(type);
var s = cm.newInstance(new Symbol(''), [str]).reflectee;
print(s);
}
You could also create a Map with registered factories for different types to avoid the need for reflection.
(not tested)
class A {
String s;
int a = 0;
int b = 0;
int c = 0;
A(this.s);
A.extended(this.s, this.a, this.b, this.c);
#override
String toString() => '${super.toString()}: $s, $a, $b, $c';
}
void main(args) {
Type t = A;
registerType(t, (List args) => new A.extended(args[0], args[1], args[2], args[3]));
...
var a = getInstance(t, ['hallo', 1, 2, 3]);
}
Map<Type,Function> _factories = {};
void registerType(Type t, Function factory) {
_factories[t] = factory;
}
void getNewInstance(Type t, List args) {
return _factories[t](args);
}

Resources