继承 (F#)
在面向对象的编程中,可使用继承来建立“属于”关系(或子类型)模型。
指定继承关系
可以使用类声明中的 inherit 关键字指定继承关系。下面的示例演示了基本的语法形式。
type MyDerived(...) =
inherit MyBase(...)
一个类最多只能有一个直接基类。如果未使用 inherit 关键字指定基类,则该类将从 Object 隐式继承。
继承成员
如果一个类是从另一个类继承的,则派生类的用户可以使用基类的方法和成员,就如同这些成员是派生类的直接成员一样。
任何 let 绑定和构造函数参数都是类私有的,因此,从派生类无法访问它们。
关键字 base 可在派生类中使用,它引用基类实例。可以像自我标识符一样使用该关键字。
虚方法和重写
与其他 .NET 语言相比,虚方法(以及属性)在 F# 中的工作方式略有不同。若要声明新的虚拟成员,请使用 abstract 关键字。无论您是否提供该方法的默认实现,都需要执行此操作。因此,基类中虚方法的完整定义遵循以下模式:
abstractmembermethod-name : type
defaultself-identifier.method-nameargument-list = method-body
而在派生类中,此虚方法的重写遵循以下模式:
overrideself-identifier.method-nameargument-list = method-body
如果省略基类中的默认实现,则基类将成为抽象类。
下面的代码示例演示了基类中新虚方法 function1 的声明,并演示了如何在派生类中重写该方法。
type MyClassBase1() =
let mutable z = 0
abstract member function1 : int -> int
default u.function1(a : int) = z <- z + a; z
type MyClassDerived1() =
inherit MyClassBase1()
override u.function1(a: int) = a + 1
构造函数和继承
必须在派生类中调用基类的构造函数。基类构造函数的参数出现在 inherit 子句内的参数列表中。必须从提供给派生类构造函数的参数来确定要使用的值。
下面的代码显示了一个基类和一个派生类,其中,派生类在 inherit 子句中调用基类构造函数:
type MyClassBase2(x: int) =
let mutable z = x * x
do for i in 1..z do printf "%d " i
type MyClassDerived2(y: int) =
inherit MyClassBase2(y * 2)
do for i in 1..y do printf "%d " i
对于多个构造函数,可以使用下面的代码。派生的类的构造函数的第一行是 inherit 子句,并且字段显示为 val 关键字声明的显式字段。更多信息请参见 Explicit Fields: The val Keyword。
type BaseClass =
val string1 : string
new (str) = { string1 = str }
new () = { string1 = "" }
type DerivedClass =
inherit BaseClass
val string2 : string
new (str1, str2) = { inherit BaseClass(str1); string2 = str2 }
new (str2) = { inherit BaseClass(); string2 = str2 }
let obj1 = DerivedClass("A", "B")
let obj2 = DerivedClass("A")
继承的替代方法
对于只需对某个类型稍作修改的情况,可考虑使用对象表达式来作为继承的替代方法。下面的示例演示了如何使用对象表达式来代替创建新的派生类型:
open System
let object1 = { new Object() with
override this.ToString() = "This overrides object.ToString()"
}
printfn "%s" (object1.ToString())
有关对象表达式的更多信息,请参见对象表达式 (F#)。
创建对象层次结构时,可考虑使用可区分联合来代替使用继承。可区分联合还可以为共享公共总体类型的不同对象的各种行为建立模型。通常,只需使用一个可区分联合,即可省去创建大量相互之间差别很小的派生类的工作。有关可区分联合的信息,请参见可区分联合 (F#)。