Compartir vía


Clases abstractas

Las clases abstractas son clases que dejan algunos o todos los miembros no implementados, de modo que las implementaciones se puedan 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, se usa una clase abstracta como clase base de una jerarquía y representa la funcionalidad común de un conjunto diverso de tipos de objetos. Como el nombre "abstracto" implica, 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 haber implementado y sin implementar miembros. 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 está marcado con la abstract 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 ese tipo. En otras palabras, el método es virtual, aunque la virtual palabra clave no se usa en F#. La palabra clave abstract 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 default palabra clave o la override palabra clave . Para obtener más información y ejemplos, vea 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 indefinidos, no use el atributo AbstractClass .

En la sintaxis anterior, el modificador de accesibilidad puede ser publico internalprivate . Para obtener más información, consulte Control de acceso.

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 inherit palabra 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 devueltos, 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 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 abstractas, métodos y propiedades. En el ejemplo, la clase abstracta Shape representa los elementos comunes del círculo y el cuadrado de las entidades concretas. Las características comunes de todas las formas (en un sistema de coordenadas bidimensionales) se abstraen en la clase Shape: la posición de 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 de 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

Consulte también