Introduction to Classes
Introduction to Classes
Fundamentals of Classes
Introduction
A class is a list of characteristics used to describe an object. In the list, each item describes a particular aspect. For example, a class used to describe a car for the motor vehicle office would have such items as the tag number, the make, the model, the year, etc.
To create a class, start with the following formula:
type class-name() =
Start with the type keyword.
After the type keyword, add a name for the class. The name of a class primarily follows the rules of names we saw for a variable. Other than that:
After the name of the class, add some (empty) parentheses and the assignment operator, =. Here is an example:
type State() =
Introduction to the Local Variables of a Class
A class must have at least one piece of information. To create such a piece of information, you can at least declare a variable after the = sign of the class. Here is an example:
type State() = let name = "North Carolina"
A typical class is not as simple as that. This means that a typical has more than one piece of information. In that case, you cannot declare more than one variable on the same line on which the class is created. In that case, after the = sign, declare the variable(s) on the next line after the = sign. In that case also, the pieces of information must be indented. Here is an example:
type State() =
let name = "North Carolina"
In the same way, you can declare other variables in the class. Here are examples:
type Vehicle() =
let tag = "485E702"
let make = "Toyota"
let model = "Corolla"
let year = 2012
The body of a class is the section that contains its pieces of information. If the class contains only one variable and that variable is declared on the same line as the class, the body is from after the = sign to the end of the line. If the variables are declared on the line after the = sign, the body of the class is from the line below = to the last indented line.
Creating an Object
As mentioned in our introduction, a class provides only the description of an object. To actually create an object, you must declare a variable of a class. To do this, type the let operator followed by a name for the object. The name follows the rules we saw for names of variables. To complete the creation of the object, assign the parenthesized name of the class. Here is an example:
type State() =
let name = "North Carolina"
let nc = State()
Since that declaration is a statement, you can end it with a(n optioanl) semicolon if you want.
When creating an oject, if you want, you can apply an operator named new to the cass object. To do this, type new between the = sign of the variable and the parenthesized name of the class. Here is an example:
type State() =
let name = "North Carolina"
let nc = new State()
Accessing the Members of a Class
One of the advantages that members of a class enjoy is that they can access each other easily, by simply using their names. For example, a method of a class can access a variable declared in the same class by simply using the name of that variable. Here are examples:
type Vehicle() = let tag = "4857" let make = "Toyota" let model = "Corolla" let year = 2012 printfn "Vehicle Information" printfn "--------------------" printfn "Tag #: %s" tag printfn "Make: %s" make printfn "Model: %s" model printfn "Year: %d" year
You may remember that the F# language provides the do keyword that allows you to perform the job of a function without explicitly creating a function. In the body of a class, you can use that keyword to perform one or more actions where you would need a function. For example, in a class, you can create a do section to perform an action, such as displaying some values. This can be done as follows:
type Vehicle() =
let tag = "4857"
let make = "Toyota"
let model = "Corolla"
let year = 2012
do
printfn "Vehicle Information"
printfn "--------------------"
printfn "Tag #: %s" tag
printfn "Make: %s" make
printfn "Model: %s" model
printfn "Year: %d" year
Remember that, at any time, to use an object, you can declare a variable of the class. Here is an example:
type Vehicle() =
let tag = "4857"
let make = "Toyota"
let model = "Corolla"
let year = 2012
do
printfn "Vehicle Information"
printfn "--------------------"
printfn "Tag #: %s" tag
printfn "Make: %s" make
printfn "Model: %s" model
printfn "Year: %d" year
let car = Vehicle()
printfn "==================="
If you had created a do section in the class as done above, that action would execute. As a result, the above code would produce:
Vehicle Information -------------------- Tag #: 4857 Make: Toyota Model: Corolla Year: 2012 =================== Press any key to close this window . . .
Early Topics on Creating a Class
Delimiting the Body of a Class
We already saw that the body of a class is the section after its = symbol. To let you explicitly indicate the body of a class, the F# language provides the class and the end keywords. To indicate the body of a class, after the = sign of the class, type class, an empty space, and type end. iables and indent them. Here is an example:
type Vehicle() = class end
The section between class and end is the body of the class. Between those two keywords, you can write the code you want. Here is an example:
type State() = class let age = 36 end
As you can see, if you need to have many code items in your class, it is not realist to write all that code on one line. The alternative is to write class and end each on its own line. Then, on the lines between class and end, write your code.
An Empty Class
Sometimes you just want to test a concept about classes but you don't need any formal functionality. In this case, you simply want an empty class. In that case, create a class with just the class and the end keywords for its body, as we saw above:
type Something() = class end
Remember that you can class on the next line after the = symbol, and write end thereafter. This can be done as follows:
type Something() =
class
end
Introduction to the Fields of a Class
As seen already, you can declare one or more variables in the body of a class. Such a variable is referred to as a field. Here is an example:
type Vehicle() =
let tag = "485E702"
let make = "Toyota"
let model = "Corolla"
let year = 2012
Remember that, if you want, you can delimit the body of a class with a class and and end lines. Between those lines, you can populate the class any way you want, such as creating fields. This can be done as follows:
type Vehicle() = class let tag = "485E702" let make = "Toyota" let model = "Corolla" let year = 2012 end
Introduction to Class Construction
Overview
In our introduction, we mentioned that a class is a technique to lay a foundation for, or to describe, an object. To start such a foundation, you must use a special type of function that holds the primary characteristics of the object. That special function is called a constructor.
A constructor of a class is a special function with some characteristics. A constructor:
In our introduction to classes, we mentioned that, when you are creating a class, you can add parentheses to the name of the class. Here is an example:
type Property() =
let price = 397_495
Remember that, when necessary, you can declare a variable of the class and use the object. Here is an example:
type Property() =
let price = 397_495
let sf = Property()
The primary way to create a constructor is to add parentheses to the name of the class, as done above. The combination of the name of the class and those parentheses is a constructor, and that constructor is referred to as the primary constructor of the class.
The Parameters of a Constructor
Adding a Parameter to a Constructor
A constructor is primarily a function. As such, it can receive arguments. To indicate that a constructor must be passed an argument, in the parentheses of the primary constructor, type the desired name of the parameter. Here is an example:
type Property(something) =
. . .
If you do this, when you create an object of the class, you must pass an argument to the class name that is assigned to the object. Here is an example:
type Vehicle(something) = let tag = "4857" let make = "Toyota" let model = "Corolla" let year = 2012 do printfn "Vehicle Information" printfn "--------------------" printfn "Tag #: %s" tag printfn "Make: %s" make printfn "Model: %s" model printfn "Year: %d" year let car = Vehicle(0)
Notice that, in the above example, neither the class nor the compiler knows the type of value that the parameter holds. As a result, in the above declaration, you can pass the argument with any value. Luckily, we didn't use that argument. As a result, the above code would produce the same result as the previous program.
As mentioned in our introduction, a constructor is a function that is created with the name of the class but the function doesn't start with the let keyword. Since you are creating a function, you must add parentheses to its name. We can start as follows:
type Property(something) =
Property()
Remember that, later on, to use a class, you will create an object of that class. That action (creating an object of a class) instructs the compiler to allocate memory for the object. In many computer languages (C++, Java, C#, Visual Basic, etc), this is done by using an operator named new (New for Visual Basic). F# also supports this operator. In a class, that keyword is defined like a function. That is, first type new. Since this operator is defined like a function, you must add parentheses to its name. To finalize the constructor, you must assign a constructor, such as the the above constructor, to it. As a result, here is an example of creating a (default) constructor:
type Property(something) =
new() = Property()
You can then add the desired field(s) to the class, as we did earlier. Here is an example:
type Property(something) =
let number = 927_571_395
let beds = 3
let baths = 2.50f
let price = 397_495
do
printfn "Property Information"
printfn "---------------------------"
printfn "Property #: %i" number
printfn "Bedrooms: %d" beds
printfn "Bathrooms: %0.2f" baths
printfn "Market Value: %d" price
new() = Property()
Remember that, in the above class, the primary constructor takes a parameter. Therefore, when creating an object of that class, you must pass an argument to the constructor. Here is an example:
type Property(something) = let number = 927_571_395 let beds = 3 let baths = 2.50f let price = 397_495 do printfn "Property Information" printfn "---------------------------" printfn "Property #: %i" number printfn "Bedrooms: %d" beds printfn "Bathrooms: %0.2f" baths printfn "Market Value: %d" price new() = Property() let house = Property(0) printfn "=========================="
This would produce:
Property Information --------------------------- Property #: 927571395 Bedrooms: 3 Bathrooms: 2.50 Market Value: 397495 ========================== Press any key to close this window . . .
A Data Type for a Parameter
If you provide a parameter to a constructor, and provide that parameter by name, the compiler would first consider that that parameter is an integer. In most case, you will want to indicate the type of the parameter. As seen in previous sections, to indicate the name of parameter, after its name, type a colon followed by the desired type. Here is an example:
type Property(number : string) =
new() = Property()
Passing Zero Arguments to a Constructor
A constructor can use more than one parameter. As always, to provide those parameters, give the name of each in the parentheses of the constructor. The names are separated by comas. There are rules you must follow for the creation of constructor:
If the name of the class has empty parentheses, this means that the class has a primary constructor. In that case, you cannot create an additional default constructor. This means that you cannot add a new() member function that has empty parentheses. As a result, the following code will produce an error:
type State() = let display _ = printfn "States Statistics" new() = State() let nc = new State() nc.display()
Obviously the solution here is to add a parameter to the primary constructor. The second rule is that, when declaring a variable of the class, you must pass an argument to the name of the class. In the body of the class, you can ignore or use the argument anyway you want. Here is an example:
type State(code : string) = do if code = "US-MO" then printfn "State Statistics" printfn "-------------------------------------" printfn "Country: United States of America" printfn "State Name: Missouri" printfn "Capital: Jefferson City" printfn "Area: 180,560 km2" elif code = "DE-BY" then printfn "State Statistics" printfn "-------------------------------------" printfn "Country: Germany" printfn "State Name: Bavaria" printfn "Capital: Munich" printfn "Area: 70_550.19 km2" else printfn "Unknow State" new() = State() let institution = State("DE-BY")
This would produce:
State Statistics ------------------------------------- Country: Germany State Name: Bavaria Capital: Munich Area: 70_550.19 km2 Press any key to close this window . . .
Here is another way to call the constructor:
type State(code : string) =
do
if code = "US-MO" then
printfn "State Statistics"
printfn "-------------------------------------"
printfn "Country: United States of America"
printfn "State Name: Missouri"
printfn "Capital: Jefferson City"
printfn "Area: 180,560 km2"
elif code = "DE-BY" then
printfn "State Statistics"
printfn "-------------------------------------"
printfn "Country: Germany"
printfn "State Name: Bavaria"
printfn "Capital: Munich"
printfn "Area: 70_550.19 km2"
else
printfn "Unknow State"
new() = State()
let institution = State("US-MO")
This would produce:
State Statistics ------------------------------------- Country: United States of America State Name: Missouri Capital: Jefferson City Area: 180,560 km2 Press any key to close this window . . .
Adding Parameters to a New() Constructor
You can add as many new() constructors as you want as long as each has a different number of parameters (than the others), including the one created using the name of the class. To add a new constructor using the new() member function, pass the desired number of parameters to both that new() member function and the class name assigned to it. In the parentheses of that class name, you must pass the number of parameters greater than or equal to the number of parameters of the new() constructor.
If the assigned class name has the same number of parameters as the new() constructor, the compiler will assign the parameters based on their positions. Here is an example:
type Property(number : int, price : int) =
. . .
new() = Property()
new(some, thing) = Property(some, thing)
Once again, in the body of the class, you can ignore or use the parameter(s). Here is an example:
type Property(number : int, price : int) = do printfn "Property Information" printfn "---------------------------" printfn "Property #: %i" number printfn "Market Value: $%d" price new(nbr, value) = Property(nbr, value) let house = Property(927_571_395, 397_495) printfn "=========================="
If the assigned class name has more parameters than the new() constructor, you must specify how the extra parameter(s) will get its (their) value(s). One way to take care of this is to assign a default value to each extra parameter (of course, there are other ways we will see). Here is an example:
type Vehicle(tag : string, make : string, model : string) =
. . .
new(tag : string, make : string, model : string, yr : int) = Vehicle(tag, make, model, yr = 1900)
Creating Different Constructors
Remember that you can add as many parameters as you want to the primary constructor of a class. In the body of the class, you can ignore or use those parameters. Here is an example:
type Property(number : int, price : int, beds : int, baths : float, basement) = do printfn "Property Information" printfn "--------------------------------" printfn "Property #: %i" number printfn "Bedrooms: %i" beds printfn "Bathrooms: %0.1f" baths printfn "Market Value: %d" price if basement = true then printfn "Finished Basement: Yes" else printfn "Finished Basement: No" let house = Property(173_046_995, 688_250, 5, 3.5, true) printfn "==============================="
This would produce:
Property Information -------------------------------- Property #: 173046995 Bedrooms: 5 Bathrooms: 3.5 Market Value: 688250 Finished Basement: Yes =============================== Press any key to close this window . . .
In a class, you can create many constructors. Each constructor must use a different number of parameters. Both the new() member function and the constructor that is assigned to it must use the same parameters. Here are examples:
type Property(...) =
...
new() = Property()
new(code, value) = Property(code, value)
new(code, value, under) = Property(code, value, under)
new(code, value, sleep, showers, under) = Property(code, value, sleep, showers, under)
When creating the primary constructor of the class, you must specify a number of parameters that corresponds to one of the new() constructors you had created in the class. For example, the aboe class has four constructors that use 0, 2, 3, or 5 parameters. Therefore, your primary constructor can use 2, 3, and 5 parameters. In the body of the class, you can use or ignore some parameters, or you can ignore or use all paramenters. Here is an example of the above class with a primary constructor that uses only three parameters:
type Property(number : int, price : int, beds : int) = do printfn "Property Information" printfn "--------------------------------" printfn "Property #: %i" number printfn "Bedrooms: %i" beds printfn "Market Value: %d" price new() = Property() new(code, value) = Property(code, value) new(code, value, under) = Property(code, value, under) new(code, value, sleep, showers, under) = Property(code, value, sleep, showers, under) let house = Property(682_957_680, 493_850, 4) printfn "==============================="
This would produce:
Property Information -------------------------------- Property #: 682957680 Bedrooms: 4 Market Value: 493850 =============================== Press any key to close this window . . .
Here is an example of the same class with a primary constructor that uses five parameters:
type Property(number : int, price : int, beds : int, baths : float, basement) = do printfn "Property Information" printfn "--------------------------------" printfn "Property #: %i" number printfn "Bedrooms: %i" beds printfn "Bathrooms: %0.1f" baths printfn "Market Value: %d" price if basement = true then printfn "Finished Basement: Yes" else printfn "Finished Basement: No" new() = Property() new(code, value) = Property(code, value) new(code, value, under) = Property(code, value, under) new(code, value, sleep, showers, under) = Property(code, value, sleep, showers, under) let house = Property(173_046_995, 688_250, 5, 3.5, true) printfn "==============================="
This would produce:
Property Information -------------------------------- Property #: 173046995 Bedrooms: 5 Bathrooms: 3.5 Market Value: 688250 Finished Basement: Yes =============================== Press any key to close this window . . .
Adding Uninitialized Members
Introduction
In your class, if you create a field using the let keyword, you must initiliaze that variable. The F# language allows you to add a member variable that is not initialized. This is done using the val keyword. To start, in the class, use the val (instead of the let) keyword to declare a variable and apply the desired data type to the variable. Here is an example:
type Customer =
val FullName : string;
A val field brings a set of requirements to a class. Normally, to use a class, you must declare a variable from it, which is naturally done using the let keyword; but you must have a constructor for the class. You have many options.
Initializing Uninitialized Members
Remember that, if your class doesn't have a primary constructor, you can create one or more constructors using the new keyword. If you create a constructor that doesn't take a parameter, you must initialize the val field with a default value, based on its type. For a natural number member, this would be 0. For a decimal number, this would be 0.00. For a Boolean variable, this would be true or false. For a character or a string, this would be an empty string. Here is an example:
type Customer =
val FullName : string;
new() = { FullName = "" }
If you want to initialize the val variable, you must add a constructor that takes a parameter that corresponds to the val field. When initializing the val member variable, assign the parameter to it. Here is an example:
type Customer =
val FullName : string;
new() = { FullName = "" }
new(name) = { FullName = name }
After doing this, you can access the val member outside the class after creating an object that using the constructor that takes a value for the val parameter. Here is an example:
type Customer = val FullName : string; new() = { FullName = "" } new(name) = { FullName = name } let client = new Customer("Jessica Constance Bernstein"); printfn "Customer Name: %s\n" client.FullName;
This would produce:
Customer Name: Jessica Constance Bernstein Press any key to continue . . .
In the same way, you can add as many val fields as you want. Remember to create a constructor that can be used to initialize the member(s) so it (they) can be accessed outside the class. Here are examples:
type Customer = val FullName : string; val PhoneNumber : string val EmailAddress : string new() = { FullName = ""; PhoneNumber = ""; EmailAddress = "" } new(name) = { FullName = name; PhoneNumber = ""; EmailAddress = "" } new(name, phone) = { FullName = name; PhoneNumber = phone; EmailAddress = "" } new(name, phone, email) = { FullName = name; PhoneNumber = phone; EmailAddress = email } let client = new Customer("Jessica Constance Bernstein", "103-118-9274", "jcbern@email.net"); printfn "Customer Name: %s" client.FullName; printfn "Phone Number: %s" client.PhoneNumber; printfn "Email Address: %s\n" client.EmailAddress;
Initializing With Default Values
An alternative to initialize a val field is to mark it as [<DefaultValue>]. The member must also be created as mutable and the class must have a primary constructor. Here is an example:
type Customer() = [<DefaultValue>] val mutable FullName : string;
After doing this, you can initialize the field with the desired value. If you are working outside the class, to start, declare a variable of the class, then use that variable to initialize the mutable val field using the < operator. Here is an example:
type Customer() = [<DefaultValue>] val mutable FullName : string; let client = new Customer(); client.FullName <- "Zachary Cox" printfn "Customer Name: %s\n" client.FullName;
This would produce:
Customer Name: Robert Press any key to continue . . .
In the same way, you can create as many val members as you want and include them in your method initializer.
|
|||
Previous | Copyright © 2012-2024, FunctionX | Monday 14 February 2022 | Next |
|