実行時ポリモーフィズムを利用するシステムは変更や拡張がしやすい。Clojureは複数の方法でポリモーフィズムをサポートしている:

  • Clojureランタイムにおけるほとんどのコアとなる基盤データ構造はJavaインターフェースで定義されている。

  • Clojureは proxy を使うことでClojureでのJavaインターフェースの生成をサポートしている( JVMにホストされた言語 参照)。

  • Clojure言語は マルチメソッド でクラスとカスタム階層の両方でのポリモーフィズムをサポートしている。

  • Clojure言語は プロトコル でポリモーフィズムのより高速な形式もサポートしている(しかし、JVMの既存の呼び出し能力を活用するためクラスのポリモーフィズムに限定されている)。

Clojureのマルチメソッドはシンプルだが実行時ポリモーフィズムのための強力なメカニズムで、OOの型と継承という罠を免れている。実行時ポリモーフィズムの背後にある基本的なアイディアは、指示者である単一の関数が、呼び出しの何らかの値に基づいて、複数の独立して定義された関数定義にディスパッチするというものだ。伝統的なシングルディスパッチのOO言語ではその値は「レシーバー」または「this」の型だ。CLOSの総称関数は、ディスパッチ値を複数の引数の型または値の組み合わせに拡張していることから、マルチメソッドだといえる。Clojureのマルチメソッドはさらに先を行っていて、ディスパッチ値を引数に対する任意の関数の結果とすることを可能にしている。Clojureは実装継承をサポートしていない。

マルチメソッドは、マルチメソッドの名前とディスパッチ関数をとる、 defmulti を使って定義する。メソッドは、マルチメソッドの名前とディスパッチ 、関数本体を渡す、 defmethod を使って独立して定義する。

(defmulti encounter (fn [x y] [(:Species x) (:Species y)]))
(defmethod encounter [:Bunny :Lion] [b l] :run-away)
(defmethod encounter [:Lion :Bunny] [l b] :eat)
(defmethod encounter [:Lion :Lion] [l1 l2] :fight)
(defmethod encounter [:Bunny :Bunny] [b1 b2] :mate)
(def b1 {:Species :Bunny :other :stuff})
(def b2 {:Species :Bunny :other :stuff})
(def l1 {:Species :Lion :other :stuff})
(def l2 {:Species :Lion :other :stuff})
(encounter b1 b2)
-> :mate
(encounter b1 l1)
-> :run-away
(encounter l1 b1)
-> :eat
(encounter l1 l2)
-> :fight

マルチメソッドはあらゆる面でfnであり、例えば map などに渡すことができる。

インターフェースに似ていて、Clojureのプロトコルは関数の仕様だけを定義し(実装は定義しない)、型が複数のプロトコルを実装することを認めている。加えて、プロトコルは後に新たな型に動的に拡張することにも開いている。プロトコルは、ネイティブのJavaのポリモーフィックなメソッド呼び出しのパフォーマンスを活用するためにクラスの型でのディスパッチだけに限定されている。より詳しくは プロトコル のページを参照。