OCaml 教程 / 对象与类(OCP)
对象与类(OCP)
OCaml 是多范式语言,同时支持函数式编程和面向对象编程(OOP)。本文介绍 OCaml 的对象系统——Object-Caml(OCP),包括对象类型、类定义、继承等核心概念。
1. 对象类型
1.1 基本对象类型
OCaml 的对象类型使用 < ... > 语法描述:
(* 对象类型声明 *)
type point = < x : int; y : int >
let p : point = object
method x = 3
method y = 4
end
let () = Printf.printf "(%d, %d)\n" p#x p#y
1.2 对象类型结构
| 语法 | 含义 | 示例 |
|---|
< m : t > | 只有方法 m | < get : int > |
< m : t; n : u > | 多个方法 | < get : int; set : int -> unit > |
< m : t; .. > | 开放对象类型 | < get : int; .. > |
< .. > | 完全开放 | 最通用的对象类型 |
(* 开放对象类型 —— 允许有更多方法 *)
type printable = < print : unit; .. >
let print_any (obj : printable) = obj#print
let p = object
method print = print_endline "hello"
method extra = 42
end
let () = print_any p (* OK: p 有 print 方法,也有 extra,但不冲突 *)
2. 方法调用
(* 方法调用使用 # 符号 *)
let p = object
method x = 3
method y = 4
method distance = sqrt (float_of_int (p#x * p#x + p#y * p#y))
end
let () = Printf.printf "distance: %f\n" p#distance
| 操作 | 语法 | 示例 |
|---|
| 方法调用 | obj#method | p#x |
| 字段访问 | 不支持直接访问 | 只能通过方法 |
| 发送消息 | 同方法调用 | Smalltalk 风格 |
3. 类定义
3.1 基本类
class point x_init y_init = object
val x = x_init
val y = y_init
method x = x
method y = y
method move dx dy = {< x = x + dx; y = y + dy >}
end
let p = new point 3 4
let p2 = p#move 1 2
3.2 类的组成元素
| 元素 | 语法 | 说明 |
|---|
| 参数 | class c arg = ... | 构造函数参数 |
| 实例变量 | val x = expr | 不可变字段 |
| 可变变量 | val mutable x = expr | 可变字段 |
| 方法 | method name = expr | 公开方法 |
| 私有方法 | private method name = expr | 仅内部调用 |
| 初始化 | initializer expr | 构造时执行 |
| 更新 | {< x = new_val >} | 返回新对象 |
class counter init = object (self)
val mutable count = init
method get = count
method increment = count <- count + 1
method reset = count <- init
method copy = {< >} (* 浅拷贝 *)
initializer
Printf.printf "Counter created with %d\n" init
end
let c = new counter 0
let () = c#increment; c#increment
let () = Printf.printf "Count: %d\n" c#get (* 2 *)
4. 继承
4.1 基本继承
class point x y = object
val x = x
val y = y
method x = x
method y = y
method distance = sqrt (float_of_int (x * x + y * y))
end
class colored_point x y color = object
inherit point x y
val color = color
method color = color
method to_string =
Printf.sprintf "(%d, %d, %s)" x y color
end
let cp = new colored_point 3 4 "red"
let () = Printf.printf "%s, distance=%f\n" cp#to_string cp#distance
4.2 方法覆盖
class animal name = object
method name = name
method speak = name ^ " makes a sound"
end
class cat name = object
inherit animal name
method! speak = name ^ " meows" (* 覆盖 *)
method purr = name ^ " purrs"
end
class dog name = object
inherit animal name
method! speak = name ^ " barks"
method fetch = name ^ " fetches"
end
⚠️ 注意点:覆盖方法时使用 method! 而不是 method,以显式表明这是覆盖。编译器会警告没有 ! 的覆盖。
5. 虚方法(Virtual Methods)
class virtual shape = object (self)
method virtual area : float
method virtual perimeter : float
method describe =
Printf.sprintf "area=%.2f, perimeter=%.2f"
self#area self#perimeter
end
class circle r = object
inherit shape
method! area = 3.14159 *. float_of_int r ** 2.0
method! perimeter = 2.0 *. 3.14159 *. float_of_int r
end
class rectangle w h = object
inherit shape
method! area = float_of_int (w * h)
method! perimeter = float_of_int (2 * (w + h))
end
| 概念 | 说明 |
|---|
method virtual m : t | 声明虚方法,不提供实现 |
class virtual c | 含虚方法的类不能实例化 |
inherit | 继承并实现虚方法 |
6. Self 类型
class widget name = object (self)
method name = name
method click = Printf.printf "%s clicked\n" name
method render = Printf.printf "Rendering %s\n" name
(* self 可以用于链式调用 *)
method with_handler handler =
handler (self : widget)
end
(* self 的类型可以通过约束来限制 *)
class type widget_type = object
method name : string
method click : unit
method render : unit
end
7. 类类型(class type)
(* 类类型 —— 类的接口 *)
class type printable = object
method print : unit
end
class type comparable = object
method compare : 'a -> int
end
(* 实现类类型 *)
class my_int n : printable = object
method print = print_int n
end
类类型 vs 对象类型
| 特性 | class type | 对象类型 |
|---|
| 用途 | 定义类的接口 | 描述对象的结构 |
| 能否实例化 | 可以用 new | 直接创建对象 |
| 支持继承 | ✅ | ❌ |
| 虚方法 | ✅ | ❌ |
8. 对象与子类型
type point = < x : int; y : int >
type color_point = < x : int; y : int; color : string >
type named_point = < x : int; y : int; name : string >
(* 子类型关系:color_point <: point *)
let p : point = (object
method x = 3
method y = 4
method color = "red"
end : color_point)
(* 显式强制转换 *)
let cp : color_point = object
method x = 3; method y = 4; method color = "red"
end
let p : point = (cp :> point) (* 强制上转 *)
⚠️ 注意点:OCaml 的对象子类型需要显式强制 (:>),不像 Java/C++ 那样隐式转换。这是为了保持类型推导的可判定性。
9. 多态变体与对象
(* 对象与多态变体的结合 *)
type 'a event = < handle : 'a >
let click_handler : [`Click of int * int] event = object
method handle = fun (`Click (x, y)) ->
Printf.printf "Click at (%d, %d)\n" x y
end
let key_handler : [`Key of char] event = object
method handle = fun (`Key c) ->
Printf.printf "Key pressed: %c\n" c
end
10. 实用设计模式
10.1 观察者模式
class ['a] observable initial = object
val mutable value = initial
val mutable observers : ('a -> unit) list = []
method get = value
method set v =
value <- v;
List.iter (fun f -> f v) observers
method subscribe f =
observers <- f :: observers
end
let counter = new observable 0
let () = counter#subscribe (fun n -> Printf.printf "New value: %d\n" n)
let () = counter#set 1 (* 输出: New value: 1 *)
let () = counter#set 2 (* 输出: New value: 2 *)
10.2 命令模式
class type command = object
method execute : unit
method undo : unit
end
class add_command (target : counter) n : command = object
method execute = target#set (target#get + n)
method undo = target#set (target#get - n)
end
and counter = object
method get : int
method set : int -> unit
end
11. 何时使用 OOP vs 函数式
| 场景 | 推荐方式 | 原因 |
|---|
| GUI 组件 | OOP | 天然适合继承层次 |
| 状态管理 | 函数式 | 不可变更安全 |
| 数据转换 | 函数式 | 模式匹配更强大 |
| 插件系统 | OOP/模块 | 多态调用 |
| 数值计算 | 函数式 | 无状态更高效 |
| 游戏实体 | OOP | 共享行为 |
| 编译器/解释器 | ADT | 代数数据类型更自然 |
💡 提示:OCaml 的 OOP 常被低估。它在 GUI 编程(如 lablgtk)和需要开放递归的场景中特别有用。大多数场景下,函数式风格更简洁高效。
12. 扩展阅读
| 资源 | 说明 |
|---|
| OCaml Manual: Objects | v2.ocaml.org/objects |
| “Object-Oriented Programming in OCaml” | ocaml.org 文档 |
| LablGTK | OCaml 的 GTK 绑定,大量使用 OOP |
| “Cautionary Tale of OOP” | 来自 Real World OCaml |
| Pierce: TAPL | Chapter 18: Subtyping |