Наследование

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

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

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

    Employee bob = Employee();
    bob.name = "Bob";
    bob.age = 42;
    bob.display();  // Name: Bob  Age: 42
} 
 
class Person{
  
    String name = "";
    int age = 0;
    void display() => print("Name: $name  Age: $age");
}
class Employee{
    String name = "";
    int age = 0;
    String company = "";
    void display() => print("Name: $name  Age: $age");
}

Здесь у нас есть класс Person, который представляет человека, и есть класс Employee, который представляет работника некоторой компании. И хотя классы замечательно работают, но мы видим, что функционал класса Person дублируется в классе Employee. И наследование позволяет избежать подобного дублирования кода.

Применим наследование:

void main (){
     
    Employee bob = Employee();
    bob.name = "Bob";
    bob.age = 42;
    bob.display();  // Name: Bob  Age: 42
} 
 
class Person{
  
    String name = "";
    int age = 0;
    void display() => print("Name: $name  Age: $age");
}
class Employee extends Person{
    String company = "";
}

Теперь класс Employee наследуется от класса Person. Для установки наследования после класса указывается ключевое слово extends и далее указывается класс, от которого идет наследование.

class Employee extends Person{

При наследовании класс Employee перенимает весь функционал класса Person - все его поля и методы и может их использовать. И также можно определить в подклассе новые поля и методы, которых нет в классе Person. В итоге код стал значительно проще, а мы можем также создать объект Employee и обращаться к его полям name и age и методу display, хотя они определены в родительском классе Person.

В этом случае класс Person еще называют родительским классом, базовым классом или суперклассом, а класс Employee - классом-наследником, подклассом или производным классом.

Конструкторы и ключевое слово super

В отличие от полей и методов конструкторы базового класса не наследуются. Если базовый класс явным образом определяет конструктор (конструктор по умолчанию не учитывается), то его необходимо вызвать в классе-наследнике при определении конструктора:

void main (){
     
    Employee bob = Employee("Bob", 42, "Google");
    bob.display();  // Name: Bob  Age: 42
} 
 
class Person{
  
    String name;
    int age;
    Person(this.name, this.age);
    void display() => print("Name: $name  Age: $age");
}
class Employee extends Person{

    String company = "";
    Employee(String name, int age, this.company): super(name, age);
}

Здесь базовый класс Person определяет конструктор, который принимает два параметра. В производном классе Employee также определяется конструктор, который вызывает конструктор базового класса, передавая ему значение параметров name и age. Для обращения к функциональности базового класса из производного применяется ключевое слово super. В частности, вызов super(name, age) фактически будет представлять обращение к конструктору базового класса Person(name, age).

Если мы не вызовем конструктор базового класса при определении конструктора в производном классе, то мы столкнемся с ошибкой.

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

void main (){
     
    Employee bob = Employee("Bob", 42, "Google");
    bob.display();  // Name: Bob  Age: 42
} 
 
class Person{
  
    String name;
    int age = 18;
    Person(this.name, this.age);
    void display() => print("Name: $name  Age: $age");
}
class Employee extends Person{

    String company = "";
    Employee(super.name, super.age, this.company);
}

В данном случае в классе Employee строка

Employee(super.name, super.age, this.company);

будет эквивалентна

Employee(String name, int age, this.company): super(name, age);

А выражение super.name фактически будет представлять значение для параметра name в конструктор базового класса Person.

Переопределение именованных конструкторов

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

void main (){
    Employee tim = Employee.withName("Tim");
    tim.display();  // Name: Tim  Age: 18
} 
 
class Person{
  
    String name;
    int age = 18;
    Person(this.name, this.age);
    // именнованный конструктор вызывает основной конструктор
    Person.withName(this.name);

    void display() =≫ print("Name: $name  Age: $age");
}
class Employee extends Person{

    String company = "";
    Employee(super.name, super.age, this.company);
    // переопределяем именнованный конструктор
    Employee.withName(String name) : super.withName(name);
}

Переопределение методов

Производные классы могут определять свои поля и методы, но также могут переопределять, изменять поведение методов базового класса. Для этого применяется аннотация @override. Так, в примерах выше класс Employee также определяет поле company для хранения компании, в которой работает работник. И было бы неплохо, если бы метод display для объекта Employee также бы выводил его компанию:

void main (){
     
    Employee bob = Employee("Bob", 42, "Google");
    bob.display();  // Name: Bob  Age: 42  Company: Google
} 
 
class Person{
  
    String name;
    int age = 18;
    Person(this.name, this.age);
    void display() => print("Name: $name  Age: $age");
}
class Employee extends Person{

    String company;
    Employee(super.name, super.age, this.company);
    // переопределяем метод display
    @override
    void display() {
        print("Name: $name  Age: $age");
        print("Company: $company");
    }
}

Вызов методов базового класса в производном

Однако в примере выше мы видим, что часть кода метода display в Employee повторяет код метода display из Person. Чтобы не повторяться, с помощью ключевого слова super мы можем просто вызвать реализацию метода display из базового класса:

void main (){
     
    Employee bob = Employee("Bob", 42, "Google");
    bob.display();
} 
 
class Person{
  
    String name;
    int age = 18;
    Person(this.name, this.age);
    void display() => print("Name: $name  Age: $age");
}
class Employee extends Person{

    String company;
    Employee(super.name, super.age, this.company);
    
    @override
    void display(){
        super.display();	// Вызов реализации из класса Person
        print("Company: $company");
    }
}

Консольный вывод:

Name: Bob  Age: 42
Company: Google
Дополнительные материалы
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850