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 |