强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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#methodp#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: Objectsv2.ocaml.org/objects
“Object-Oriented Programming in OCaml”ocaml.org 文档
LablGTKOCaml 的 GTK 绑定,大量使用 OOP
“Cautionary Tale of OOP”来自 Real World OCaml
Pierce: TAPLChapter 18: Subtyping