Clases abstractas

Las clases abstractas son clases que dejan algunos o todos los miembros sin implementar, por lo que las implementaciones se pueden proporcionar mediante clases derivadas.

Sintaxis

// Abstract class syntax.
[<AbstractClass>]
type [ accessibility-modifier ] abstract-class-name =
[ inherit base-class-or-interface-name ]
[ abstract-member-declarations-and-member-definitions ]

// Abstract member syntax.
abstract member member-name : type-signature

Observaciones

En la programación orientada a objetos, una clase abstracta se usa como clase base de una jerarquía y representa la funcionalidad común de un conjunto diverso de tipos de objeto. Como implica el nombre "abstract", las clases abstractas a menudo no se corresponden directamente con entidades concretas en el dominio del problema. Sin embargo, representan lo que muchas entidades concretas diferentes tienen en común.

Las clases abstractas deben tener el AbstractClass atributo . Pueden tener miembros implementados y no implementados. El uso del término abstract cuando se aplica a una clase es el mismo que en otros lenguajes .NET; sin embargo, el uso del término abstract cuando se aplica a métodos (y propiedades) es un poco diferente en F# de su uso en otros lenguajes .NET. En F#, cuando un método se marca con la palabra clave , esto indica que un miembro tiene una entrada, conocida como ranura de distribución virtual, en la tabla interna de funciones virtuales para abstract ese tipo. En otras palabras, el método es virtual, aunque la virtual palabra clave no se usa en F#. La palabra abstract clave se usa en métodos virtuales independientemente de si se implementa el método . La declaración de una ranura de distribución virtual es independiente de la definición de un método para esa ranura de distribución. Por lo tanto, el equivalente de F# de una declaración y definición de método virtual en otro lenguaje .NET es una combinación de una declaración de método abstracto y una definición independiente, con la palabra clave o la palabra default override clave . Para obtener más información y ejemplos, vea Métodos.

Una clase se considera abstracta solo si hay métodos abstractos que se declaran pero no se definen. Por lo tanto, las clases que tienen métodos abstractos no son necesariamente clases abstractas. A menos que una clase tenga métodos abstractos no definidos, no use el atributo AbstractClass.

En la sintaxis anterior, accessibility-modifier puede ser public , o private internal . Para obtener más información, consulta Access Control.

Al igual que con otros tipos, las clases abstractas pueden tener una clase base y una o varias interfaces base. Cada clase base o interfaz aparece en una línea independiente junto con la palabra inherit clave .

La definición de tipo de una clase abstracta puede contener miembros totalmente definidos, pero también puede contener miembros abstractos. La sintaxis de los miembros abstractos se muestra por separado en la sintaxis anterior. En esta sintaxis, la firma de tipo de un miembro es una lista que contiene los tipos de parámetro en orden y los tipos de valor devuelto, separados por tokens o tokens según corresponda para los parámetros curried y -> * tupled. La sintaxis de las firmas de tipo de miembro abstracto es la misma que la que se usa en los archivos de firma y la que muestra IntelliSense en el Editor de Visual Studio Code.

En el código siguiente se muestra una clase abstracta Shape, que tiene dos clases derivadas no abstractas, Square y Circle. En el ejemplo se muestra cómo usar clases, métodos y propiedades abstractos. En el ejemplo, la clase abstracta Shape representa los elementos comunes del círculo y cuadrado de entidades concretas. Las características comunes de todas las formas (en un sistema de coordenadas bidimensional) se abstraen en la clase Shape: la posición en la cuadrícula, un ángulo de rotación y las propiedades de área y perímetro. Se pueden invalidar, excepto la posición, el comportamiento de las formas individuales que no pueden cambiar.

El método de rotación se puede invalidar, como en la clase Circle, que es invariable debido a su simetría. Por lo tanto, en la clase Circle, el método de rotación se reemplaza por un método que no hace nada.

// An abstract class that has some methods and properties defined
// and some left abstract.
[<AbstractClass>]
type Shape2D(x0 : float, y0 : float) =
    let mutable x, y = x0, y0
    let mutable rotAngle = 0.0

    // These properties are not declared abstract. They
    // cannot be overriden.
    member this.CenterX with get() = x and set xval = x <- xval
    member this.CenterY with get() = y and set yval = y <- yval

    // These properties are abstract, and no default implementation
    // is provided. Non-abstract derived classes must implement these.
    abstract Area : float with get
    abstract Perimeter : float  with get
    abstract Name : string with get

    // This method is not declared abstract. It cannot be
    // overridden.
    member this.Move dx dy =
       x <- x + dx
       y <- y + dy

    // An abstract method that is given a default implementation
    // is equivalent to a virtual method in other .NET languages.
    // Rotate changes the internal angle of rotation of the square.
    // Angle is assumed to be in degrees.
    abstract member Rotate: float -> unit
    default this.Rotate(angle) = rotAngle <- rotAngle + angle

type Square(x, y, sideLengthIn) =
    inherit Shape2D(x, y)
    member this.SideLength = sideLengthIn
    override this.Area = this.SideLength * this.SideLength
    override this.Perimeter = this.SideLength * 4.
    override this.Name = "Square"

type Circle(x, y, radius) =
    inherit Shape2D(x, y)
    let PI = 3.141592654
    member this.Radius = radius
    override this.Area = PI * this.Radius * this.Radius
    override this.Perimeter = 2. * PI * this.Radius
    // Rotating a circle does nothing, so use the wildcard
    // character to discard the unused argument and
    // evaluate to unit.
    override this.Rotate(_) = ()
    override this.Name = "Circle"

let square1 = new Square(0.0, 0.0, 10.0)
let circle1 = new Circle(0.0, 0.0, 5.0)
circle1.CenterX <- 1.0
circle1.CenterY <- -2.0
square1.Move -1.0 2.0
square1.Rotate 45.0
circle1.Rotate 45.0
printfn "Perimeter of square with side length %f is %f, %f"
        (square1.SideLength) (square1.Area) (square1.Perimeter)
printfn "Circumference of circle with radius %f is %f, %f"
        (circle1.Radius) (circle1.Area) (circle1.Perimeter)

let shapeList : list<Shape2D> = [ (square1 :> Shape2D);
                                  (circle1 :> Shape2D) ]
List.iter (fun (elem : Shape2D) ->
              printfn "Area of %s: %f" (elem.Name) (elem.Area))
          shapeList

Salida:

Perimeter of square with side length 10.000000 is 40.000000
Circumference of circle with radius 5.000000 is 31.415927
Area of Square: 100.000000
Area of Circle: 78.539816

Vea también