Clases abstractas

Las clases abstractas son aquellas que dejan sin implementar algunos miembros o todos ellos para que las clases derivadas puedan proporcionar las implementaciones.

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

Comentarios

En la programación orientada a objetos, las clases abstractas se usan como clases bases de una jerarquía y representan la funcionalidad común de un conjunto diverso de tipos de objetos. Como sugiere el término "abstracto" de su nombre, estas clases a menudo no se corresponden directamente con entidades concretas del dominio del problema. Aun así, representan algo que muchas entidades concretas diferentes tienen en común.

Las clases abstractas deben tener el atributo AbstractClass. Pueden tener miembros implementados y no implementados. El uso del término abstracto aplicado a una clase es el mismo que en otros lenguajes .NET, pero cuando se aplica a métodos (y propiedades), el uso del término abstracto es un poco diferente en F# con respecto a otros lenguajes .NET. En F#, cuando un método está marcado con la palabra clave abstract, significa que un miembro tiene una entrada (conocida como entrada de distribución virtual) en la tabla interna de funciones virtuales para ese tipo. En otras palabras, el método es virtual, aunque en F# no se usa la palabra clave virtual. En los métodos virtuales se usa la palabra clave abstract, independientemente de si se implementa el método. La declaración de una entrada de distribución virtual es independiente de la definición de un método para esa entrada 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 default o override. Para obtener más información y ejemplos, consulte Métodos.

Una clase solo se considera abstracta si hay métodos abstractos declarados, pero no definidos. 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, private o 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 o interfaz base aparece en una línea independiente junto con la palabra clave inherit.

La definición de tipo de una clase abstracta puede contener miembros totalmente definidos, pero también puede incluir miembros abstractos. La sintaxis de los miembros abstractos se muestra por separado en la sintaxis anterior. En esta sintaxis, la signatura de tipo de un miembro es una lista que contiene los tipos de parámetro en orden y los tipos devueltos, separados por tokens -> o *, según corresponda para los parámetros currificados y organizados en tuplas. La sintaxis de las signaturas de tipo de miembro abstracto es la misma que la que se usa en los archivos de signatura y 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 de las entidades concretas de círculo y cuadrado. 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. Todas se pueden invalidar excepto la posición, cuyo comportamiento las formas individuales no pueden cambiar.

El método de rotación se puede invalidar, como en la clase Circle, que es invariable en lo que respecta a la rotación 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