I'm creating objects dynamically from Map data, populating fields for matching key names. The problem comes when fields are defined on the parent, where attempting to set a value on a parent field produces the error:
No static setter 'name' declared in class 'Skill'.
NoSuchMethodError : method not found: 'name'
code:
class Resource {
String name;
String description;
Resource.map(Map data)
{
ClassMirror c = reflectClass(runtimeType);
ClassMirror thisType = c;
while(c != null)
{
for (var k in c.declarations.keys) {
print('${MirrorSystem.getName(k)} : ${data[MirrorSystem.getName(k)]}');
if(data[MirrorSystem.getName(k)] != null)
{
thisType.setField(k, data[MirrorSystem.getName(k)]);
}
}
c = c.superclass;
}
}
}
class Skill extends Resource
{
Skill.map(data) : super.map(data);
}
You should use a ObjectMirror to set a field on your object. Your code tries to set a field on ClassMirror which tries to define a static variable.
class Resource {
String name;
String description;
Resource.map(Map data)
{
ObjectMirror o = reflect(this); // added
ClassMirror c = reflectClass(runtimeType);
ClassMirror thisType = c;
while(c != null)
{
for (var k in c.declarations.keys) {
print('${MirrorSystem.getName(k)} : ${data[MirrorSystem.getName(k)]}');
if(data[MirrorSystem.getName(k)] != null)
{
// replace "thisType" with "o"
o.setField(k, data[MirrorSystem.getName(k)]);
}
}
c = c.superclass;
}
}
}
class Skill extends Resource
{
Skill.map(data) : super.map(data);
}
Static methods/fields are not inherited in Dart.
There were already some discussions about that behavior here.
You can take a look at the answer to this question in Dart, using Mirrors, how would you call a class's static method from an instance of the class?
If the methods/fields you try to access are not static please provide more code (the classes/objects you are reflecting about)
Related
This question already has answers here:
"The operator can’t be unconditionally invoked because the receiver can be null" error after migrating to Dart null-safety
(3 answers)
Closed 12 months ago.
I have migrated my Dart code to NNBD / Null Safety. Some of it looks like this:
class Foo {
String? _a;
void foo() {
if (_a != null) {
_a += 'a';
}
}
}
class Bar {
Bar() {
_a = 'a';
}
String _a;
}
This causes two analysis errors. For _a += 'a';:
An expression whose value can be 'null' must be null-checked before it can be dereferenced.
Try checking that the value isn't 'null' before dereferencing it.
For Bar() {:
Non-nullable instance field '_a' must be initialized.
Try adding an initializer expression, or add a field initializer in this constructor, or mark it 'late'.
In both cases I have already done exactly what the error suggests! What's up with that?
I'm using Dart 2.12.0-133.2.beta (Tue Dec 15).
Edit: I found this page which says:
The analyzer can’t model the flow of your whole application, so it can’t predict the values of global variables or class fields.
But that doesn't make sense to me - there's only one possible flow control path from if (_a != null) to _a += 'a'; in this case - there's no async code and Dart is single-threaded - so it doesn't matter that _a isn't local.
And the error message for Bar() explicitly states the possibility of initialising the field in the constructor.
The problem is that class fields can be overridden even if it is marked as final. The following example illustrates the problem:
class A {
final String? text = 'hello';
String? getText() {
if (text != null) {
return text;
} else {
return 'WAS NULL!';
}
}
}
class B extends A {
bool first = true;
#override
String? get text {
if (first) {
first = false;
return 'world';
} else {
return null;
}
}
}
void main() {
print(A().getText()); // hello
print(B().getText()); // null
}
The B class overrides the text final field so it returns a value the first time it is asked but returns null after this. You cannot write your A class in such a way that you can prevent this form of overrides from being allowed.
So we cannot change the return value of getText from String? to String even if it looks like we checks the text field for null before returning it.
An expression whose value can be 'null' must be null-checked before it can be dereferenced. Try checking that the value isn't 'null' before dereferencing it.
It seems like this really does only work for local variables. This code has no errors:
class Foo {
String? _a;
void foo() {
final a = _a;
if (a != null) {
a += 'a';
_a = a;
}
}
}
It kind of sucks though. My code is now filled with code that just copies class members to local variables and back again. :-/
Non-nullable instance field '_a' must be initialized. Try adding an initializer expression, or add a field initializer in this constructor, or mark it 'late'.
Ah so it turns out a "field initializer" is actually like this:
class Bar {
Bar() : _a = 'a';
String _a;
}
There are few ways to deal with this situation. I've given a detailed answer here so I'm only writing the solutions from it:
Use local variable (Recommended)
void foo() {
var a = this.a; // <-- Local variable
if (a != null) {
a += 'a';
this.a = a;
}
}
Use ??
void foo() {
var a = (this.a ?? '') + 'a';
this.a = a;
}
Use Bang operator (!)
You should only use this solution when you're 100% sure that the variable (a) is not null at the time you're using it.
void foo() {
a = a! + 'a'; // <-- Bang operator
}
To answer your second question:
Non-nullable fields should always be initialized. There are generally three ways of initializing them:
In the declaration:
class Bar {
String a = 'a';
}
In the initializing formal
class Bar {
String a;
Bar({required this.a});
}
In the initializer list:
class Bar {
String a;
Bar(String b) : a = b;
}
You can create your classes in null-safety like this
class JobDoc {
File? docCam1;
File? docCam2;
File? docBarcode;
File? docSignature;
JobDoc({this.docCam1, this.docCam2, this.docBarcode, this.docSignature});
JobDoc.fromJson(Map<String, dynamic> json) {
docCam1 = json['docCam1'] ?? null;
docCam2 = json['docCam2'] ?? null;
docBarcode = json['docBarcode'] ?? null;
docSignature = json['docSignature'] ?? null;
}
}
In dart we can execute some code when value of field is changed using something like
class Name{
String fname;
String lname;
}
class Person extends ChangeNotifier{
Name _name = Name();
set name(Name n){
notifyListeners();
_name = n;
}
get name=>_name;
}
//inside main()
Person p = Person();
p.name = Name();
I want to be able to perform similar action while setting inner fields. Such as while doing
p.name.fname ="FooBar";
But I want to be able to do it from Person class.
Because I am extending ChangeNotifier in Person class. And I want to call
notifyListeners()
that is not accessible in Name class. This is best I've come up with
Name newName = Name(p.name); //copy constructor
newName.fname = "Foo Bar";
p.name = newName;
Is there a better way?
What you can do depends on how you can constrain the API.
If Name objects are routinely being created by third-party code and passed around, and are expected to retain their identity when stored in a Person object, then here isn't much you can do. So I wouldn't design the Person object that way.
Instead I'd say that the Name object of a Person object is linked to that, and setting the name of a Person is the same as setting both name parts.
Example:
class Person {
_PersonName _name;
Person(...) : ... {
_name = _PersonName(this);
}
...
void set name(Name name) {
_name.fname = name.fname;
_name.lname = name.lname;
notifyListeners();
}
Name get name => _name;
}
class _PersonName extends Name {
final Person _owner;
_PersonName(this._owner);
void set fname(String fname) {
super.fname = fname;
_owner.notifyListeners();
}
void set lname(String lname) {
super.lname = lname;
_owner.notifyListeners();
}
}
That has the disadvantage that the extracted _PersonName is forever linked to the Person object, even if you try to write a different Name object.
Another option is to create a new _PersonName on every store a new name object, and detach the old object from the Person at that point:
class Person {
_PersonName _name = _PersonName;
Person(...) : ... {
_name = _PersonName(this, null, null);
}
void set name(Name name) {
_name.owner = null;
_name = _PersonName(this, name.fname, name.lname);
notifyListeners();
}
Name get name => _name;
}
class _PersonName extends Name {
Person _owner;
_PersonName(this._owner, String fname, String lname) {
super.fname = fname;
super.lname = lname;
}
void set fname(String fname) {
super.fname = fname;
owner?.notifyListeners();
}
void set lname(String lname) {
super.lname = lname;
owner?.notifyListeners();
}
}
This approach behaves mostly like the plain storing of name objects, except that if you do:
var p = Person();
var n = Name();
p.name = n;
print(identical(n, p.name)); // false?
you don't preserve the identity of the Name object stored into the Person object.
There is no way to do so, and also change the behavior of setting strings directly on the name using person.name.fname = ..., so something has to be sacrificed.
Is it really not possible to create multiple constructors for a class in dart?
in my Player Class, If I have this constructor
Player(String name, int color) {
this._color = color;
this._name = name;
}
Then I try to add this constructor:
Player(Player another) {
this._color = another.getColor();
this._name = another.getName();
}
I get the following error:
The default constructor is already defined.
I'm not looking for a workaround by creating one Constructor with a bunch of non required arguments.
Is there a nice way to solve this?
You can only have one unnamed constructor, but you can have any number of additional named constructors
class Player {
Player(String name, int color) {
this._color = color;
this._name = name;
}
Player.fromPlayer(Player another) {
this._color = another.getColor();
this._name = another.getName();
}
}
new Player.fromPlayer(playerOne);
This constructor
Player(String name, int color) {
this._color = color;
this._name = name;
}
can be simplified to
Player(this._name, this._color);
Named constructors can also be private by starting the name with _
class Player {
Player._(this._name, this._color);
Player._foo();
}
Constructors with final fields initializer list are necessary:
class Player {
final String name;
final String color;
Player(this.name, this.color);
Player.fromPlayer(Player another) :
color = another.color,
name = another.name;
}
If your class uses final parameters the accepted answer will not work. This does:
class Player {
final String name;
final String color;
Player(this.name, this.color);
Player.fromPlayer(Player another) :
color = another.color,
name = another.name;
}
If you already used a constructor with params in the project and now you figured out that you need some no params default constructor you can add an empty constructor.
class User{
String name;
User({this.name}); //This you already had before
User.empty(); //Add this later
}
Try the below code on DartPad
class MyClass {
//These two are private attributes
int _age;
String _name;
//This is a public attribute
String defaultName = "My Default Name!";
//Default constructor
MyClass() {
_age = 0;
_name = "Anonymous";
}
MyClass.copyContructor(MyClass fromMyClass) {
this._age = fromMyClass._age;
this._name = fromMyClass._name;
}
MyClass.overloadedContructor(String name, int age) {
this._age = age;
this._name = name;
}
MyClass.overloadedContructorNamedArguemnts({String name, int age}) {
this._age = age;
this._name = name;
}
//Overriding the toString() method
String toString() {
String retVal = "Name:: " + _name + " | " + "Age:: " + _age.toString();
return retVal;
}
}
//The execution starts from here..
void main() {
MyClass myClass1 = new MyClass();
//Cannot access oprivate attributes
//print(myClass1.name);
//print(myClass1.age);
//Can access the public attribute
print("Default Name:: " + myClass1.defaultName);
print(myClass1.toString());
MyClass myClass2 = new MyClass.copyContructor(myClass1);
print(myClass2.toString());
MyClass myClass3 = new MyClass.overloadedContructor("Amit", 42);
print(myClass3.toString());
MyClass myClass4 =
new MyClass.overloadedContructorNamedArguemnts(age: 42, name: "Amit");
print(myClass4.toString());
}
Dart doesn't support parameter overloading (having multiple functions of the same name but with different parameters). This applies to constructors as well - that's the reason why in SDK there're so many classes with named constructors.
In Dart you can use Default Constructor, Named Constructor, Factory Method and Static Method to instantiate classes:
class A {
// Default constructor
A() : msg = '1';
// Named constructor with positional param
A.message(this.msg);
// Factory method with named param
factory A.underscore({String msg = ''}) {
return A.message('_'+msg);
}
// Factory method with arrow func body
static A bang(msg) => A.message('!'+msg);
final String msg;
}
void main() {
print(A().msg);
print(A.message('2').msg);
print(A.underscore(msg: '3').msg);
print(A.bang('4').msg);
}
Output:
1
2
_3
!4
You can use factory constructors
factory Player.fromPlayer(Player another) => Player(another.name, another.color);
i had found solution to solve this problem depend on checked the type of data you are passed it to function
Try this Solution
As Günter Zöchbauer already specified in his answer:
You can only have one unnamed constructor, but you can have any number of additional named constructors in Flutter.
By using named constructor you can create multiple constructors in the same class.
Each constructor will have a unique name. So that you can identify each of them.
Syntax for named constructor :
class_name.constructor_name (arguments) {
// If there is a block of code, use this syntax
// Statements
}
or
class_name.constructor_name (arguments);
// If there is no block of code, use this syntax
For more insights Click Here
To know about various types of constructors in Flutter Click Here
Class User{
User();
User.fromName(this.name);
String? name;
}
If you want to do some more elaborated property calculation (I'm a Swift guy), you can do like this:
class FooProvider {
int selectedFoo;
FooProvider(List<String> usageObjects)
: selectedFoo = firstOne(usageObjects);
static int firstOne(List<String> usageObjects) {
return 2;
}
}
Using Dart here.
As the above title suggests, I have a class (shown below) that has three bool instance variables. What I want to do is create a function that inspects the identifier names of these instance variables and prints each of them out in a string. The .declarations getter that comes with the ClassMirror class ALMOST does this, except it also gives me the name of the Constructor and any other methods I have in the class. This is no good. So really what I want is a way to filter by type (i.e., only give me the boolean identifiers as strings.) Any way to do this?
class BooleanHolder {
bool isMarried = false;
bool isBoard2 = false;
bool isBoard3 = false;
List<bool> boolCollection;
BooleanHolder() {
}
void boolsToStrings() {
ClassMirror cm = reflectClass(BooleanHolder);
Map<Symbol, DeclarationMirror> map = cm.declarations;
for (DeclarationMirror dm in map.values) {
print(MirrorSystem.getName(dm.simpleName));
}
}
}
OUTPUT IS:
isMarried
isBoard2
isBoard3
boolsToStrings
BooleanHolder
Sample code.
import "dart:mirrors";
void main() {
var type = reflectType(Foo);
var found = filter(type, [reflectType(bool), reflectType(int)]);
for(var element in found) {
var name = MirrorSystem.getName(element.simpleName);
print(name);
}
}
List<VariableMirror> filter(TypeMirror owner, List<TypeMirror> types) {
var result = new List<VariableMirror>();
if (owner is ClassMirror) {
for (var declaration in owner.declarations.values) {
if (declaration is VariableMirror) {
var declaredType = declaration.type;
for (var type in types) {
if (declaredType.isSubtypeOf(type)) {
result.add(declaration);
}
}
}
}
}
return result;
}
class Foo {
bool bool1 = true;
bool bool2;
int int1;
int int2;
String string1;
String string2;
}
Output:
bool1
bool2
int1
int2
I need to perform some initialization when new instances of my domain class are created.
class ActivationToken {
String foo
String bar
}
When I do this I want bar to be initialized by code inside ActivationToken:
def tok = new ActivationToken(foo:'a')
I cannot see how to 'override' the 'constructor' to make this happen. I know in this case I could just add a normal constructor but this is just a simple example.
The map constructor is coming from Groovy - not Grails in this case. I did some experimentation, and this is what I came up with:
class Foo {
String name = "bob"
int num = 0
public Foo() {
this([:])
}
public Foo(Map map) {
map?.each { k, v -> this[k] = v }
name = name.toUpperCase()
}
public String toString() {
"$name=$num"
}
}
assert 'BOB=0' == new Foo().toString()
assert 'JOE=32' == new Foo(name:"joe", num: 32).toString()
Basically, it appears that you'll have to manually override the constructors if you need to process the property after construction.
Alternately, you can override individual setters, which is cleaner and safer in general:
class Foo {
String name = "bob"
int num = 0
public void setName(n) {
name = n.toUpperCase()
}
public String toString() {
"$name=$num"
}
}
assert 'bob=0' == new Foo().toString()
assert 'JOE=32' == new Foo(name:"joe", num: 32).toString()
Note that the default value isn't processed, but that should be OK in most instances.
The solution above is also good for cases where initializing an object from parameters in a web request, for example, where you wish to ignore extraneous values, catching Missing property exceptions.
public Foo(Map map) {
try {
map?.each { k, v -> this[k] = v }
}
catch(Exception e){
}
}