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;
}
}
Related
I remember that Dart objects have a method which does an object return a value by default without pointing to an property. Example:
class A {
final String name;
A(this.name);
...
}
main() {
var obj = A('chesu');
print(obj + ' locuaz');
}
Output: chesu locuaz
But I don't remember that method or decorator and it is not toString().
Use interpolation to compose strings and values. It will automatically call toString method of value.
class A {
A(this.name);
final String name;
#override
toString() => name;
}
main() {
var obj = A('chesu');
print('$obj locuaz');
}
I finally found what I was looking for, it's called callable classes.
Is it possible to invoke setters which declaired with set keyword in class in Dart?
In the script written below, Dart does not recognize the setters:
class Cat {
String _size = '';
String _color = '';
int _age = 0;
Cat(String initialSize, String initialColor, int initialAge) {
size(initialSize);
color(initialColor);
age(initialAge);
}
// getter and setter fields are started
String get size {
return _size;
}
void set size(String animalSize) {
_size = animalSize;
}
String get color {
return _color;
}
void set color(String animalColor) {
_color = animalColor;
}
int get age {
return _age;
}
void set age(int animalAge) {
_age = animalAge;
}
// getter and setter fields are ended
}
Error: 'size' isn't a function or method and can't be invoked.
Error: 'color' isn't a function or method and can't be invoked.
Error: 'age' isn't a function or method and can't be invoked.
The way you are calling a setter/getter is how you would do an assignment of the field. So your code should be:
size = initialSize;
color = initialColor;
age = initialAge;
This will call the setter of size, color and age.
The point of having getter/setter is to be able to simulate a field but have some logic behind that field other than just direct access to a variable.
Unrelated to the question: I don't know if your class is just an example but your current class could be written as the following:
class Cat {
String size;
String color;
int age;
Cat(this.size, this.color, this.age);
}
Of if you want named parameters:
class Cat {
String size;
String color;
int age;
Cat({required this.size, required this.color, required this.age});
}
It is in general not needed to have getter/setters unless you need to have some logic or want to add restrictions. The great thing about getter/setter in Dart is that they behave like fields so we can just start by having fields and replace the field with a getter and/or setter later if we need to add extra logic.
Dart is pass-by and return-by reference, meaning that if I have a class which contains a member instance of another class, and I want to return this using a get function, the caller of this function has direct access to mutate the data inside that member instance.
For instance, take the following example:
class Mother {
String _name;
Mother() {
_name = "Default";
}
String get name {
return _name;
}
set name (String name) {
_name = name;
}
}
class Child {
Mother _mother = Mother();
Mother get mother {
return _mother;
}
}
void main() {
Child child = Child();
Mother localMother = child.mother;
localMother.name = "Samantha";
print(localMother.name); // Prints Samantha - expected
print(child.mother.name); // Prints Samantha - bad. The original instance inside child was mutated, without the ability for me to react inside my setter
}
How can I prevent the caller of child.mother from modifying the data inside the mother class?
Something like this will make sure the user gets an error on compile time:
class Mother {
String _name;
Mother() : _name = "Default";
String get name {
return _name;
}
}
class _Mother extends Mother {
set name(String name) {
_name = name;
}
}
class Child {
_Mother _mother = _Mother();
Mother get mother {
return _mother;
}
}
void main() {
Child child = Child();
Mother localMother = child.mother;
localMother.name = "Samantha"; // There isn’t a setter named 'name' in class 'Mother'.
print(localMother.name); // Prints Samantha - expected
print(child.mother
.name); // Prints Samantha - bad. The original instance inside child was mutated, without the ability for me to react inside my setter
}
An alternative design (which are used a lot in the Dart SDK) is to make something like this. A problem here is we cannot give a compile error about the use of set since the exception are thrown at runtime. The same behavior can be observed when using UnmodifiableListView from dart:collection:
abstract class Mother {
String get name;
set name(String name);
factory Mother() => _Mother();
}
class _Mother implements Mother {
String _name;
_Mother() {
_name = "Default";
}
String get name {
return _name;
}
set name(String name) {
_name = name;
}
}
class MotherView implements Mother {
final Mother _src;
MotherView(this._src);
#override
String get name => _src.name;
#override
set name(String name) {
throw UnsupportedError('Setting name');
}
}
class Child {
Mother _mother = Mother();
Mother get mother => MotherView(_mother);
}
void main() {
Child child = Child();
Mother localMother = child.mother;
localMother.name = "Samantha"; // UnsupportedError: Setting name
print(localMother.name); // Prints Samantha - expected
print(child.mother
.name); // Prints Samantha - bad. The original instance inside child was mutated, without the ability for me to react inside my setter
}
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.
Basically, that's what I'm trying to do:
ClassName
{
final OtherClass field;
ClassName()
{
field = new OtherClass(this);
}
}
It's not possible to assign a final field in a constructor body. The final field needs to be assigned before the constructor body, in the initializer list or on declaration:
class ClassName
{
final OtherClass field = new OtherClass(); // Here
ClassName()
: field = new OtherClass() // or here
{
}
}
As you can't use this in the initializer list or on the declaration, you can't do what you plan to do.
With null safety, you can initialize a final field in different ways:
At declaration:
class Foo{
final int bar = 1;
}
In constructor parameter (initializing formal).
class Foo {
final int bar;
// Initializing in constructor parameter.
Foo(this.bar);
}
In the initializer list.
class Foo {
final int bar;
// Initializer list
Foo() : bar = 1;
}
Combination of above two.
class Foo {
final int bar;
Foo(int value) : bar = value;
}
Use late keyword for lazy initialization.
class Foo {
late final int bar; // Initialize it later, maybe in a method
}
Since Dart 2.12 it is possible by using late keyword.
The code below prints 5:
class ClassName
{
final int var1 = 5;
late final OtherClass field;
ClassName()
{
field = new OtherClass(this);
}
}
class OtherClass {
OtherClass(ClassName object) {
print(object.var1);
}
}
void main() {
final object = ClassName();
}
Please see this and the following sections