Introduction to Class Inheritance
Introduction to Class Inheritance
Fundamentals
Introduction
Class inheritance is the ability to create a class that is based on another class, or to create new classes that get their primary functionalities from a certain class. The primary class is referred to as the parent or base class. A child class that is based on the parent class is also said to be derived from it. A child class can have only one parent class.
Before indicating an inheritance, you must have a parent class. You can use one of the many built-in classes available from the .NET Framework (in reality, not all classes can be used as parents).
Inheritance is done using the inherit keyword. The basic formula to inherit from a known class is:
type new-class-name = inherit parent-class-name body
Inheritance and the Primary Constructor
There are various ways to perform class inheritance. The most fundamental way is to use the primary constructor of the parent class. The formula to follow is:
type new-class-name(parameter(s)) = inherit parent-class-name(parameter(s))
With an existing class in mind, to perform inheritance, specify the name of the new class you are creating as new-class-name. The new class can (should) be followed by parentheses that would include one or more parameters. Add the = symbol followed by the inherit keyword. Then, call the primary constructor of the parent class and pass to it the argument(s) created in the parentheses of the new class. This can be done as follows:
type Circle(rad) =
member this.Radius = rad
type Sphere(rad) =
inherit Circle(rad)
The Body of a Child Class
A child class is primarily a class like any other. It has a body. The body of the child class starts after the primary construtor in the child class:
type new-class-name(parameter(s)) = inherit parent-class-name(parameter(s)) body
Properties in a Child Class
In the body of a child class, you can create the behavior you want. For example, you can create one or more properties. Remember that a property is created with the member keyword.
Using a Child Class
You can use a child class just like any other: declare a variable and access the member(s) of the class. Here is an example
type Circle(rad) = member this.Radius = rad type Sphere(rad) = inherit Circle(rad) let ball = new Sphere(63.7384); printfn "Sphere Characteristics"; printfn "----------------------"; printfn "Radius: %f" ball.Radius; printfn "======================";
This would produce:
Sphere Characteristics ---------------------- Radius: 63.738400 ====================== Press any key to close this window . . .
In the above example, we used a parent class that has a primary constructor that takes one parameter. In the same way, you can use a parent class that uses more than one parameter. In the child class, you can make the primary constructor take the same number of parameters as the parent class. After the inherit keyword, call the primary constructor of the parent class and pass the parameters specified in the primary constructor of the child class. When creating an object of the child class, pass the parameters of the primary constructor of the child class. This can be done as follows:
type Property(number, value) = member self.PropertyCode = number member self.MarketValue = value type SingleFamily(number, value) = inherit Property(number, value) let house = new SingleFamily(295_370_849, 475840) printfn "House Description"; printfn "-------------------------" printfn "Property #: %i" house.PropertyCode printfn "Market Value: $%d" house.MarketValue printfn "=========================";
This would produce:
House Description ------------------------- Property #: 295370849 Market Value: $475840 ========================= Press any key to close this window . . .
You can also make the primary constructor of the child class have more parameters than the primary constructor of the parent class. In this class, after the inherit keyword in the child class, call the primary constructor of the parent class. Pass the parameters that correspond to those of the primary constructor of the parent class. Here is an example:
type Person(first, last) = member me.FirstName : string = first member me.LastName : string = last type Customer(first, last, acntNumber)= inherit Person(first, last)
In the body of the child class, you can create a member property and assign one of the extra parameters. Here is an example:
type Person(first, last) =
member me.FirstName : string = first
member me.LastName : string = last
type Customer(first, last, acntNumber)=
inherit Person(first, last)
member me.AccountNumber : string = acntNumber;
let client = new Customer("Aaron", "Hansman", "106-03-7384");
printfn "Kolo Bank - Customer Record";
printfn "First Name: %s" client.FirstName;
printfn "Last Name: %s" client.LastName;
printfn "Account #: %s\n" client.AccountNumber;
This would produce:
Kolo Bank - Customer Record First Name: Aaron Last Name: Hansman Account #: 106-03-7384 Press any key to close this window . . .
To give you access to the members of the parent class from the child class, the F# language provides the base keyword. To use it, type base followed by a period and the parent member you want to access (you may remember that members declared with the let keyword in the parent class are private; as a result, such members are not accessible in the child class). Here are examples:
type Person(first, last) = member this.FirstName : string = first member me.LastName : string = last type Customer(first, last, acntNumber) = inherit Person(first, last) member me.AccountNumber : string = acntNumber; member me.FullName = base.LastName + ", " + base.FirstName; let client = new Customer("Frank", "Justice", "148-95-8350"); printfn "Kolo Bank - Customer Record"; printfn "Full Name: %s" client.FullName; printfn "Account #: %s\n" client.AccountNumber;
This would produce:
Kolo Bank - Customer Record Full Name: Justice, Frank Account #: 148-95-8350 Press any key to close this window . . .
Transitive Inheritance
Just as a child class can inherit from a parent class, another class can inherit from the child class, making such a new class a grand-child of the first parent class. When a class C inherits from a class B that itself inherits from a class A, class C inherits the (public) members from both class B (its own parent) and from class A (its grand-parent). This is illustrated in the following example. It works as follows. This is a financial bank application where customer records are created from a Person class. A customer object gets its foundational information from the Person class. A typical employee (who works for the bank) also has a bank account. This means that an employee is also a customer, which means an employee record primarily has the exact same pieces of information as a customer, which makes an Employee class inherit from the Customer. On the other hand, an employee record has details that the bank doesn't need from customer, such as a salary or an employment status:
type EmploymentStatus = | Unknown = 0 | FullTime = 1 | PartTime = 2 | Intern = 3 | Seasonal = 4 type Person(first, last) = member me.FirstName : string = first member me.LastName : string = last type Customer(first, last, acntNumber)= inherit Person(first, last) member me.AccountNumber : string = acntNumber; member me.FullName = base.LastName + ", " + base.FirstName; type Employee(emplNumber, first, last, acntNumber, employmentStatus, salary) = inherit Customer(first, last, acntNumber) member me.EmployeeNumber : string = emplNumber; member me.EmploymentStatus : EmploymentStatus = EmploymentStatus.FullTime member me.Salary : int = salary let client = new Customer("Frank", "Justice", "6148-795-8350"); let staff = new Employee("9702-9741", client.FirstName, client.LastName, client.AccountNumber, EmploymentStatus.FullTime, 68000); printfn "Kolo Bank - Employee Record"; printfn "First Name: %s" client.FirstName; printfn "Last Name: %s" client.LastName; printfn "Employee #: %s" staff.EmployeeNumber; printfn "Employment Status #: %A" staff.EmploymentStatus; printfn "Yearly Salary: %i" staff.Salary; printfn "Bank Account #: %s" client.AccountNumber;
This would produce:
Kolo Bank - Employee Record First Name: Frank Last Name: Justice Employee #: 9702-9741 Employment Status #: FullTime Yearly Salary: 68000 Bank Account #: 6148-795-8350 Press any key to close this window . . .
Methods in a Derived Class
Probably the most valuable feature of class inheritance is that a child class can acquire new behavior not available from the parent class. One of the options you have is to add new methods (and properties) to the new class. For example, you can add constructors to anticipate various ways to create objects from the derived class. Here is an example:
type EmploymentStatus = | Unknown = 0 | FullTime = 1 | PartTime = 2 | Intern = 3 | Seasonal = 4 type Person(first, last) = member my.FirstName : string = first member my.LastName : string = last type Customer(first, last, acntNumber) = inherit Person(first, last) member my.AccountNumber : string = acntNumber member my.FullName = base.LastName + ", " + base.FirstName new() = Customer("John", "Doe", "0000-000-0000") new(first, last) = Customer(first, last, "0000-000-0000") type Employee(emplNumber, first, last, acntNumber, employmentStatus, salary) = inherit Customer(first, last, acntNumber) member my.EmployeeNumber : string = emplNumber; member my.EmploymentStatus : EmploymentStatus = EmploymentStatus.FullTime member my.Salary : int = salary new() = Employee("0000-0000", "John", "Doe", "0000-000-0000", EmploymentStatus.Unknown, 0) new(first, last) = Employee("0000-0000", first, last, "0000-000-0000", EmploymentStatus.Unknown, 0) new(emplNumber, first, last) = Employee(emplNumber, first, last, "0000-000-0000", EmploymentStatus.Unknown, 0) new(emplNumber, first, last, acntNumber) = Employee(emplNumber, first, last, acntNumber, EmploymentStatus.Unknown, 0)
You can use such constructors when creating an object. In the same way, you can expand a child class anyway you see fit.
Object Casting
Introduction
We are already familiar with conversion functions that are used to convert a value from one type to another. Here is an example that call the int() function to convert a floating point number to an integer:
let a = 628.305
let b = int a;
printfn "Number: %.03f" a;
printfn "Number: %d" b;
The most important issue about conversion is that the types must be compatible. The conversion primarily follows the logic by which the number of bits used by, or to hold the values of, a type A is compatible, that is reconcialable, with the number of bits used by, or to hold the values of, a type B. In this case, the value of type A can be converted to a type B. As a result, if the values are not compatible, or the bits are not reconcialable, the conversion will fail.
Just as conversion is possible among values of primitive types, conversion is also possible among objects created from classes. Just as primitive conversion must follow compatible types, object conversion must follow compatibility. When it comes to objects, conversion is based on class hierarchy, which means that inheritance must be established between two classes. Therefore, when it comes to classes, conversion is referred to as casting. Two options exist: downcasting and upcasting.
Based on a relationship between a child class and its parent, an object created from a child class can be converted to the parent class. This is referred to as up-casting or upcasting. The casting is carried by the :> operator. The formula to follow is:
derived-object :> parent-class
In this formula, the derived-object can be a variable declared from a class that is derived from another class. The parent-class is a class alligned in the upper ancestry of the class from which the variable was declared. Here is an example:
type Person(first, last) =
member this.FirstName : string = first;
member this.LastName : string = last;
type Customer(first, last, acntNumber)=
inherit Person(first, last)
member this.AccountNumber : string = acntNumber;
member this.FullName = base.LastName + ", " + base.FirstName;
let client = new Customer("Frank", "Justice", "6148-795-8350");
let individual = client :> Person
printfn "Kolo Bank - Customer as a Person";
printfn "First Name: %s" individual.FirstName;
printfn "Last Name: %s\n" individual.LastName;
This would produce:
Kolo Bank - Customer as a Person First Name: Frank Last Name: Justice Press any key to close this window . . .
In the same way, an object created from any way down a hierarchy can be cast to a class up the hierarchy. Here is an example:
type EmploymentStatus = | Unknown = 0 | FullTime = 1 | PartTime = 2 | Intern = 3 | Seasonal = 4 type Person(first, last) = member this.FirstName : string = first member this.LastName : string = last type Customer(first, last, acntNumber)= inherit Person(first, last) member this.AccountNumber : string = acntNumber; member this.FullName = base.LastName + ", " + base.FirstName; type Employee(emplNumber, first, last, acntNumber, employmentStatus, salary) = inherit Customer(first, last, acntNumber) member this.EmployeeNumber : string = emplNumber; member this.EmploymentStatus : EmploymentStatus = EmploymentStatus.FullTime member this.Salary : int = salary let staffMember = new Employee("9792-4085", "Jonathan", "Lawson", "3741-957-9407", EmploymentStatus.PartTime, 48000); let persFirstName = (staffMember :> Person).FirstName; let persLastName = (staffMember :> Person).LastName; let clientFirstName = (staffMember :> Customer).FirstName; let clientLastName = (staffMember :> Customer).LastName; let clientAcntNumber = (staffMember :> Customer).AccountNumber; printfn "Kolo Bank - Employee as Person" printfn "First Name: %s" persFirstName; printfn "Last Name: %s\n" persLastName; printfn "Kolo Bank - Customer as a Person"; printfn "First Name: %s" clientFirstName; printfn "Last Name: %s" clientLastName; printfn "Account #: %s\n" clientAcntNumber;
This would produce:
Kolo Bank - Employee as Person First Name: Jonathan Last Name: Lawson Kolo Bank - Customer as a Person First Name: Jonathan Last Name: Lawson Account #: 3741-957-9407 Press any key to close this window . . .
When upcasting, if you are using Microsoft Visual Studio, the Intellisense will show the members of the upper-class:
Down-casting is the ability to convert an object to a class that is a child of the one that served to create the object. The operator used to perform this operation is :?>. The formula to follow is:
some-object :?> Child-Class
As you can imagine, down-casting is the opposite to up-casting. Up-casting is easy as we know that, except for the Object class of the .NET Framework (the Object class is the ultimate parent or ancestor to all classes of your programs in F#), every class has a parent, but not every class has a child. This means that, when you decide to down-cast, the compiler must find out whether the class used to create your object has a child. For this reason, the compiler waits until the application executes to resolve the compatibility issue. For this reason, down-casting may require some gymnastics.
To down-cast, you can first create an object, up-cast it to the appropriate parent class, then down-cast as necessary. This can be done as follows:
type EmploymentStatus =
| Unknown = 0
| FullTime = 1
| PartTime = 2
| Intern = 3
| Seasonal = 4
type Person(first, last) =
member this.FirstName : string = first
member this.LastName : string = last
type Customer(first, last, acntNumber) =
inherit Person(first, last)
member this.AccountNumber : string = acntNumber
member this.FullName = base.LastName + ", " + base.FirstName
new(first, last) = Customer(first, last, "0000-000-0000")
type Employee(emplNumber, first, last, acntNumber, employmentStatus, salary) =
inherit Customer(first, last, acntNumber)
member this.EmployeeNumber : string = emplNumber
member this.EmploymentStatus : EmploymentStatus = EmploymentStatus.FullTime
member this.Salary : int = salary
new(first, last) = Employee("0000-0000", first, last, "0000-000-0000", EmploymentStatus.Unknown, 0)
new(emplNumber, first, last) = Employee(emplNumber, first, last, "0000-000-0000", EmploymentStatus.Unknown, 0)
new(emplNumber, first, last, acntNumber) = Employee(emplNumber, first, last, acntNumber, EmploymentStatus.Unknown, 0)
let empl = new Employee("8204-5083", "Meghan", "Walley", "2735-038-4058")
// Getting a Customer side from an Employee object
let client = empl :> Customer;
// Getting the customer information from an Employee child object
let customer = client :?> Employee
printfn "Kolo Bank - Customer Record from Employee Information"
printfn "=----------------------------------------------------"
printfn "First Name: %s" customer.FirstName
printfn "Last Name: %s" customer.LastName
printfn "Account #: %s\n" customer.AccountNumber
This would produce:
Kolo Bank - Customer Record from Employee Information =---------------------------------------------------- First Name: Meghan Last Name: Walley Account #: 2735-038-4058
|
|||
Previous | Copyright © 2009-2024, FunctionX | Monday 14 February 2022 | Next |
|