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

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

Язык F# поддерживает наследование, то есть мы можем унаследовать функционал одного класса другим классом. Наследование реализуется с помощью ключевого слова inherit. Например:

type Person() = class end
type Employee() = inherit Person()

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

Для установки наследования в производном классе выполнить оператор inherit, после которого указывается вызов конструктора базового класса. Так, в классе Employee в примере выше как раз происходит вызов конструктора класса Person

inherit Person()

Наследование устанавливает отношение is a или является. То есть в примере выше класс Employee определяет новый класс, но в то же время объект этого класса также может выступать в качестве объекта класса Person. Другими слова, работник предприятия (Employee) также является человеком (Person). Например:

type Person() = class end
type Employee() = inherit Person()

let displayInfo(p: Person) = printfn "Объект класса Person"

let tom = Person()
let bob = Employee()

displayInfo(tom)    // передача объекта Person
displayInfo(bob)    // передача объекта Employee

Здесь определена функция displayInfo(), которая в качестве параметра принимает объект Person. Но поскольку объекты Employee также представляют класс Employee, то мы можем их передавать в эту функцию.

let bob = Employee()
displayInfo(bob)    // передача объекта Employee

Наследование функционала класса

Для чего нужно наследование? Установка наследования позволяет сократить объем кода. Например, пусть в базовом классе Person будет определен какой-нибудь функционал:

type Person(name, age) = 
    member this.Name = name
    member this.Age = age
    member this.Print() = printfn $"Person name: {this.Name}  age: {this.Age}"
 
type Employee(name, age) = inherit Person(name, age)
 
let bob = Employee("Bob", 31)
bob.Print()
printfn $"name: {bob.Name}"

Здесь класс Person, который представляет человека, определяет два свойства Name и Age для хранения имени и возраста соответственно. И также имеет метод для вывода этой информации на консоль. Класс Employee представляет условного работника предприятия. Но работник предприятия (в зависимости от задачи) тоже может иметь имя и возраст и метод для вывода информации. И чтобы заново не определять один и тот же функционал в другом классе, легче его унаследовать от уже имеющегося. Таким образом, класс Employee здесь также будет обладать свойствами Name и Age и методом Print.

Стоит отметить, что поскольку класс Person имеет конструктор с двумя параметрами, то класс Employee при вызове этого конструктора естественно должен передать для них значения. И для этого он сам определяет конструктор с двумя параметрами и передает их значения в конструктор Person.

Наследование в классе с несколькими конструкторами

Производный класс при наследовании должен вызвать конструктор базового класса. Но что если производный класс имеет несколько конструкторов?

type Person(name, age) = 
    member this.Name = name
    member this.Age = age
    member this.Print() = printfn $"Person name: {this.Name}  age: {this.Age}"
 
type Employee(name, age) = 
    inherit Person(name, age)
    new(name) = Employee(name, 18)
    new() = Employee("Undefined", 18)

В классе Employee реализовано три конструктора: один первичный, два дополнительных. В первичном конструкторе вызывается конструктор базового класса. Остальные конструкторы по сути восходят к вызову первичного конструктора. Поэтому в данном случае проблем при наследовании не возникнет.

Расширение базового класса

Как правило, производный класс не только наследует имеющийся функционал, но и расширяет его - дополняет своими свойствами, методами, полями. Например:

type Person(name, age) = 
    member this.Name = name
    member this.Age = age
    member this.Print() = printfn $"Person name: {this.Name}  age: {this.Age}"
 
type Employee(name, age, company) = 
    inherit Person(name, age)
    member this.Company = company
    member this.Work() = printfn $"{this.Name} works"
 
let bob = Employee("Bob", 31, "Microsoft")
bob.Work()                          // Bob works
printfn $"Company: {bob.Company}"   // Company: Microsoft

В данном случае класс Employee дополнительно определяет свойство Company для хранения места работы и метод Work - имитацию процесса работы.

Доступ к функционалу базового класса

Стоит обратить внимание, что производных класс не имеет доступа к тем компонентам базового класса, которые имеют модификатор доступа private. Например:

type Person(_name, _age) = 
    let mutable name = _name
    let mutable age = _age
    member _.Print() = printfn $"Person name: {name}  age: {age}"
 
 
type Employee(_name, _age, _company) = 
    inherit Person(_name, _age)
    member this.Company = _company
    member this.GetEmpInfo() = printfn $"Person name: {name}  age: {age}"    // Ошибка! поля name и age недоступны

Поля, определяемые с помощью оператора let, по умолчанию имеют модификатор доступа private, поэтому они доступны только внутри своего класса. А производный класс Employee к ним доступа не имеет. То же самое относится и к методам и свойствам, которые определены с модификатором private - они также недоступны для производного класса.

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