Конструкторы

Последнее обновление: 02.04.2023

Для создания объекта класса и инициализации состояния объекта применяются специальные методы, которые называются конструкторами. По умолчанию, если класс не определеяет явным образом конструкторы, то для него автоматички создается конструктор по умолчанию, который не принимает никаких параметров:

class Person{ 

    String name = "";		// имя
	int age = 0;			// возраст
    void display() =>print("Name: $name \tAge: $age");
}

void main() {
     
    Person tom = Person();  // используем конструктор по умолчанию
    tom.display();	// Name:   Age: 0
}

Но мы также можем определить свои конструкторы. Определение своих конструкторов позволяет нам передать полям класса определенные значения или какую-то определенную логику, которая будет выполняться при создании объекта. Например:

class Person{ 

    String name = "";
	int age = 0;
    // конструктор
    Person(String personName, int personAge) 
    {
        name = personName;
        age = personAge;
    }

    void display() => print("Name: $name \tAge: $age");
}

void main() {
     
    Person tom = Person("Tom", 38); // используем наш конструктор
    tom.display();  // Name: Tom       Age: 38
}

Теперь в классе определен коструктор, который принимает два параметра и с их помощью устанавливает значения полей объекта. Конструктор должен называться по имени класса:

Person(String personName, int personAge) 
{
    name = personName;
    age = personAge;
}

Поскольку мы определили свой конструктор, для класса больше не генерируется конструктор по умолчанию. И для создания объекта Person нам надо использовать наш конструктор:

Person tom = Person("Tom", 38);

В итоге уже при создании объект получит начальные данные для своих полей. Консольный вывод программы:

Name: Tom  Age: 38

Подобным образом можно создавать и другие объекты:

void main() {
     
    Person tom = Person("Tom", 38);
    tom.display();  // Name: Tom       Age: 38

    Person bob = Person("Bob", 42);
    bob.display();  // Name: Bob       Age: 42

    Person sam = Person("Sam", 25);
    sam.display();  // Name: Sam       Age: 25
}

И благодаря конструктору с параметрами каждый объект уже при создании будет инициализирован каками-то своими данными.

Ключевое слово this

Ключевое слово this представляет ссылку на текущий экземпляр класса. Через это ключевое слово мы можем обращаться к переменным, методам объекта, а также вызывать его конструкторы. В какой ситуации мы можем использовать это слово? Посмотрим на конструктор класса Person:

Person(String personName, int personAge) 
{
    name = personName;
    age = personAge;
}

В данном случае параметры конструктора называются personName и personAge. Возможно, не самые лучшие названия. И, возможно, мы бы хотели, чтобы параметры назывались также, как и поля, для которых передаются значения. Что-то наподобие:

Person(String name, int age) 
{
    name = name;
    age = age;
}

Но в данном случае названия параметров "скрывают" названия переменных. И фактически получается, что мы присваиваем параметрам name и age значения этих же параметров. И чтобы разграничить поля и параметры, можно применить ключевое слово this:

class Person{

	String name = "";
	int age = 0;
	
    Person(String name, int age)
    {
        this.name = name;
        this.age = age;
    }
    void display(){
        print("Name: $name \tAge: $age");
    }
}

Так, в данном случае указываем, что значение параметра name присваивается полю name. Выражение this.name представляет обращение именно к полю класса, а не к параметру.

Сокращенная версия констуктора

Также используя ключевое слово this, мы можем сократить определение конструктора:

class Person{

	String name;
	int age;
	
    Person(this.name, this.age);
	
    void display(){
        print("Name: $name \tAge: $age");
    }
}
void main() {
     
    Person tom = Person("Tom", 38);
    tom.display();  // Name: Tom       Age: 38
}

Когда будет происходить вызов конструктора:

Person tom = Person("Tom", 38);

То строка "Tom" будет передаваться в this.name, а число 38 - в this.age.

Стоит отметить, что при таком определении конструктора нам необязательно задавать для полей класса начальные значения.

Именованные конструкторы

По умолчанию мы можем определить только один общий конструктор. Если же нам необходимо использовать в классе сразу несколько конструкторов, то в этом случае нужно применять именованные конструкторы (named constructors). Для этого к имени класса через точку добавляется дополнительный идентификатор (произвольное имя):

имя_класса.идентификатор()

Фактически идентификатор выступает в качестве имени конструктора. Например, добавим в класс Person пару именованных конструкторов:

class Person{

	String name = "";
	int age = 0;
	
    Person.undefined(){
        name = "undefined";
        age = 18;
    }
    Person.withName(String name){
        this.name = name;
        age = 18;
    }
    Person(String name, int age)
    {
        this.name = name; 
        this.age = age;
    }
	
    void display(){
        print("Name: $name \tAge: $age");
    }
}

void main() {
     
    Person bob = Person.undefined();      // вызов первого конструктора без параметров
    bob.display();

    Person tom = Person.withName("Tom"); // вызов второго конструктора с одним параметром
    tom.display();
          
    Person sam = Person("Sam", 25); // вызов третьего конструктора с двумя параметрами
    sam.display();
}

В данном случае определено два дополнительных конструктора: Person.undefined без параметров и Person.withName с одним параметром для установки имени. При вызове подобных конструкторов необходимо указывать их полное имя:

Person tom = Person.withName("Tom");

Консольный вывод программы:

Name: undefined         Age: 18
Name: Tom       Age: 18
Name: Sam       Age: 25

Вызов цепочки конструкторов

В примере с именованными конструкторами выше применялось три конструктора, которые выполняют идентичные действия: устанавливают поля name и age. Чтобы избежать повторов, с помощью this можно вызвать один из конструкторов класса и передать для его параметров необходимые значения. В итоге мы сможем определить несколько конструкторов, которые принимают разное количество параметров и по цепочке вызывают друг друга:

class Person{

	String name;
	int age;

	Person.undefined(): this("undefined", 18);
	
	Person.withName(String name): this(name, 18);
	
    Person(this.name, this.age);
	
    void display(){
        print("Name: $name \tAge: $age");
    }
}

void main() {
     
    Person bob = Person.undefined();      // вызов первого конструктора без параметров
    bob.display();

    Person tom = Person.withName("Tom"); // вызов второго конструктора с одним параметром
    tom.display();
          
    Person sam = Person("Sam", 25); // вызов третьего конструктора с двумя параметрами
    sam.display();
}

В данном случае конструкторы Person.undefined и Person.withName вызывают третий, в котором собственно будут устанавливаться поля name и age. Поскольку этот третий конструктор определен в сокращенной форме, то для полей класса можно не устанавливать значения по умолчанию.

Обязательные и необязательные параметры

Поскольку конструктор фактически представляет функцией, то как и у любой функции, в нем можно определять обязательные и необязательные параметры:

class Person{

	String name;
	int age;
	
    Person({required this.name, this.age=18});
	
    void display(){
        print("Name: $name \tAge: $age");
    }
}

void main() {

    Person tom = Person(name:"Tom");  
    tom.display();          // Name: Tom       Age: 18
          
    Person sam = Person(name:"Sam", age:25);
    sam.display();          // Name: Sam       Age: 25
}

В данном случае параметры name и age конструктора являются именнованными. Причем параметр name является обязательным, а для age установлено значение по умолчанию.

Инициализаторы

Инициализаторы представляют способ инициализации полей класса:

class Person{

	String name ="";
	int age =0;
	
    Person(String name, int age) : this.name=name, this.age = age {
		print("Person $name created");
	}
	
    void display(){
        print("Name: $name \tAge: $age");
    }
}

void main() {

    Person tom = Person("Tom", 38);  
    tom.display();          // Name: Tom       Age: 38
}

Список инициализации указывает после параметров конструктора через двоеточие до открывающей фигурной скобки:

Person(String name, int age) : this.name=name, this.age = age {

Обычно списки инициализации используют параметры конструктора для установки значений полей. При этом конструктор может выполнять какую-то другую работу.

При этом при инициализации полей можно задать какую-нибудь динамическую логику:

Person(String name, int age) : this.name=name, this.age = age + 1 {
		print("Person $name created");
}

Стоит учитывать, что при использовании инициализаторов сначала выполняется инициализация полей в списках инициализации и только затем выполняется логика в конструкторе:

Person(String name, int age) : this.name=name, this.age = age {
	
    this.age = 22;
}

В данном случае значение поля age будет равно 22, так как конструктор перезапишет значение, установленое инициализатором.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850