An Object's Interface

Introduction

An interface is a list of items that can be used to create a class. The interface itself is not a class; it only provides a list of actions that a class would need.

Like a class or a structure, an interface has members but those members are not defined. Each member simply indicates its name but also provides such information as its type(s) of argument(s), if any, and its return type.

Creating an Interface

To create an interface, type type followed by a name. By tradition, the name of an interface starts with I (i in uppercase). After the name of the interface, type the = symbol.

The Body of an Interface

The section after the = symbol is the body of the interface. In that body, you can create the desired behavior of the interface.

An Empty Interface

The body of an interface can start with the interface keyword. In that case, you must end the body with the end keyword. Here is an example:

type ICircular = interface end

Notice that there is no member in the body of this interface. Such an interface is referred to as empty. You can create such an interface to test something without the need to formally create an interface.

Populating an Interface

An interface can contain just one member. In that case, you can create that member just after the = symbol that introduces the body of the interface. Here is an example:

type ICircular = ...

As mentioned already, you can delimit the body of the interface with interface and end. Here is an example:

type ICircular = interface ... end

In most cases (not all cases), an interface contains more than one member. In such a situation, the body of the interface must start on the next line. Here is an example:

type ICircular =
    ...

As a result, the common formula to create an interface is:

type interface-name =
  [ interface ]
  	abstract member-1 : [ data-type 1 -> ] return-type-1
  	abstract member-2 : [ data-type 2 -> ] return-type-2
  	abstract member_n : [ data-type_n -> ] return-type_n
  [ end ]

Again, you can delimit the body of the interface with interface and end. Here is an example:

type ICircular =
    interface
    ...
    end

Introduction to the Members of an Interface

An Abstract Member in an Interface

In the body of the interface, you can create the members you want. To start, you can create each member as an abstraction. In this case, start the member with the abstract keyword. Add a name for the members. If you are creating a property, add a colon and a data type. Here is an example:

type ICircular =
    interface
        abstract Area : float
    end

Implementing an Interface

Once an interface exists, any class that needs its functionality must imprement it. The formula to follow to implement an interface is:

type class-name[(parameter(s))] =
        member(s)
    interface interface-name with
        interface-member(s)

To implement an interface, in the body of the class, after the local members of the class, type interface, followed by the name of the interface, and followed by with. After this and on the next line, implement the member(s) of the interface. Here is an example:

type ICircular =
    interface
        abstract member Circumference : unit -> double
        abstract Area : unit -> double
    end

type Circle(radius : double) =
    member this.Radius : double = radius;
        interface ICircular with
        member this.Circumference() = radius * 2.00 * 3.14156
        member this.Area() : double = radius * radius * 3.14156

Using an Interface

Introduction

To use the functionaity of an interface, you must cast an object created from a class implementer to its interface. There are various techniques you can use.

Up-Casting an Object

After implementing an interface in a class, you can create an object of the class by declaring a variable from that class. To access a method from the interface, declare another variable in that same class but you must up-cast it to the interface. Here is an example:

type ICircular =
    abstract Circumference : unit -> double
    abstract Area : unit -> double

type Circle(radius : double) =
    member this.Radius : double = radius;
    interface ICircular with
        member this.Circumference() = radius * 2.00 * 3.14156;
        member this.Area() : double = radius * radius * 3.14156;

let circ = new Circle(28.14);
let disc = circ :> ICircular;

printfn "Circle Characteristics";
printfn "----------------------";
printfn "Radius:        %f" circ.Radius;
printfn "Circumference: %f" (disc.Circumference());
printfn "Area:          %f" (disc.Area());

Localling Casting an Object

Instead of first declaring a variable, you can also use a local cast. Here are examples:

type ICircular =
    abstract Circumference : unit -> double
    abstract Area : unit -> double

type Circle(radius : double) =
    member this.Radius : double = radius;
    interface ICircular with
        member this.Circumference() = radius * 2.00 * 3.14156;
        member this.Area() : double = radius * radius * 3.14156;

let circ = new Circle(28.14);
let around = (circ :> ICircular).Circumference();

printfn "Circle Characteristics";
printfn "-------------------------";
printfn "Radius:        %f" circ.Radius;
printfn "Circumference: %f" around;
printfn "Area:          %f\n" ((circ :> ICircular).Area());

This would produce:

Circle Characteristics
-------------------------
Radius:        28.140000
Circumference: 176.806997
Area:          2487.674445

Press any key to continue . . .

Up-Casting a Method

As another technique, when implementing an interface in a class, after defining a method of the interface, create another member function that holds the same name as the method. To define that other method, create an upcast and call the method of the interface. The formula to follow is:

member | self-identifier | interface-name].interface-method() = ([self-identifier | interface-name] :> interface-name).interface-method()

Start with the member keyword. It is followed by either a self-identifier (this, self, a letter or word of your choice, or else) or the name of the interface. This is followed by a period and the name of the method from the interface with its parentheses and the = sign. Add some parentheses. In the parentheses, use the same option you chose for the Self identifier or the name of the interface. The same Self identifier must be used on both sides. If you decide to use the name of the interface, you must use it on both sides. After the up-cast, access the name of the method using a period and end it with parentheses. Once this has been done, you can access the interface method from an object created from the class. Here are examples:

type ICircular =
    abstract Circumference : unit -> double
    abstract Area : unit -> double

type Circle(radius : double) =
    member this.Radius : double = radius
              member me.Circumference() = (me :> ICircular).Circumference()
    member ICircular.Area()        = (ICircular :> ICircular).Area()
    interface ICircular with
        member this.Circumference() = radius * 2.00 * 3.14156
        member this.Area() : double = radius * radius * 3.14156

let circ = new Circle(46.8506);

printfn "Circle Characteristics";
printfn "-------------------------";
printfn "Radius:        %f" circ.Radius;
printfn "Circumference: %f" (circ.Circumference());
printfn "Area:          %f\n" (circ.Area());

This would produce:

Circle Characteristics
-------------------------
Radius:        46.850600
Circumference: 294.367942
Area:          6895.657349

Press any key to continue . . .

Implementing Many Interfaces

F# doesn't support multiple inheritance, which is the ability to create a class that is directly based on many classes. As an alternative, you can create a class that implements more than one interface.

The formula to implement many interfaces is:

type class-name[(parameter(s))] =
    member(s)
    interface interface-name 1 with
  	interface-member(s) 1
    interface interface-name 2 with
  	interface-member(s) 2
    . . .
    interface interface-name n with
  	interface-member(s) n

In the body of the class, each interface has its implementation section. The individual implementations follow the techniques we have used so far. To access the member of an interface from the object created using the class, you can declare a variable that is up-cast from the interface. This can be done as follows:

type ICircular =
    abstract Circumference : unit -> double
    abstract Area : unit -> double

type IQuadrilateralArea =
    abstract Area : double * double -> double
    
type Circle(radius : double) =
    member this.Radius : double = radius;
    member me.Circumference() = (me :> ICircular).Circumference();
    member ICircular.Area()   = (ICircular :> ICircular).Area();
    interface ICircular with
        member this.Circumference() = radius * 2.00 * 3.14156;
        member this.Area() : double = radius * radius * 3.14156;
    
type Cylinder(radius : double, height : double) =
    member this.Radius : double = radius
    member this.Height : double = height
    member me.Circumference() = (me :> ICircular).Circumference()
    member ICircular.BottomArea()   = (ICircular :> ICircular).Area()
    interface ICircular with
        member this.Circumference() = radius * 2.00 * 3.14156
        member this.Area() : double = radius * radius * 3.14156
    interface IQuadrilateralArea with
        member this.Area(length : double, width : double) : double = length * width

let cup = new Cylinder(35.74, 22.82);
let around = cup :> ICircular;
let lateral = cup :> IQuadrilateralArea;

printfn "Ice Cream Cup Characteristics";
printfn "-------------------------";
printfn "Radius:        %0.2f" cup.Radius;
printfn "Circumference: %0.2f" (around.Circumference());
printfn "Bottom Area:   %0.2f" (cup.BottomArea());
printfn "Lateral Area:  %0.2f\n" (lateral.Area(around.Circumference(), cup.Height));

This would produce:

Ice Cream Cup Characteristics
-------------------------
Radius:        35.74
Circumference: 224.56
Bottom Area:   4012.86
Lateral Area:  5124.43

Press any key to continue . . .

Properties in an Interface

A property is called abstract if it is not defined. Any class that wants to use that property must implement it.


Previous Copyright © 2014-2024, FunctionX Monday 14 February 2022 Next