数据类型和协议

栏目: 编程语言 · Clojure · 发布时间: 6年前

内容简介:让一个新的类型来实现已有的接口;让一个已有的类型实现一个新的接口,而不用重新编译现有代码。Clojure 里面接口的对应物称之为协议(Protocol)。一个协议由一个或者多个方法组成,而且每个方法可以有多个方法体。所有的方法至少有一个参数,这个参数对应对应到 Java 里面的 this。

让一个新的类型来实现已有的接口;让一个已有的类型实现一个新的接口,而不用重新编译现有代码。

协议

Clojure 里面接口的对应物称之为协议(Protocol)。

一个协议由一个或者多个方法组成,而且每个方法可以有多个方法体。所有的方法至少有一个参数,这个参数对应对应到 Java 里面的 this。

协议里没每个方法的第一个参数是特殊的,因为到底使用这个协议的哪个实现体就是根据第一个参数来决定的。即根据第一个参数的类型来决定的。

协议的定义如下所示:

(defprotocol Test
  (say-hello [this arg1 arg2]))

协议的命名方式是驼峰命名风格的,因为它们最终会被编译成 JVM 的接口和类。

扩展已有的类型

通过 extend-protocol 可以对已有的类型进行扩展

extend-protocol 是一个宏,这个宏的第一个参数是协议的名字,紧接着是一些符号(比如指定类型名字)以及列表(前面指定的类型对于协议的方法的实现)。

extend-protocol 不是对一个协议进行扩展的唯一办法,扩展的方法还有:

  • 内联实现

  • extend

  • extend-type

extend-type 跟 extend-protocol 是相对的:extend-protocol 把一个协议扩展到多个类型,而 extend-type 则可以把多个协议扩展到一个类型。编写它们的代码结构是类似的,只是指定协议与指定类型的地方相互调换一下就可以了。

还可以把一个协议扩展到 nil。

定义类型

一个 Clojure 类型是一个 Java 类,不过定义 Clojure 的类型要简单很多:

(defrecord Point [x y])
;or
(deftype Point [x y])

这两种定义方法都会定义出一个新的 Point Java 类,Java 类里面有两个以 public 和 final 修饰的名为 x 和 y 字段。跟协议一样,类型的名字是采用驼峰风格的。要创建一个新的 Point 实例,直接调用它的构造函数 (Point. 3 4) 就可以了。

定义的每一个字段都是 Java 中的 Object 类型。

;为字段指定类型
(defrecord Testx [^String name ^int age])

deftype 与 defrecord 的区别

defrecord 对于所定义的类型提供了与 Clojure 以及 Java 进行互操作的一些默认行为,而 deftype 则提供了一些对于底层操作进行优化的能力。

记录

通常被称为记录类型,由 defrecord 定义的类型其实就是由 deftype 定义的类型的一种特例。添加了如下这些额外的特性:

  • 值语义
  • 实现了关系型数据结构的接口
  • 元数据的支持
  • 对于 Clojure reader 的支持,比如可以直接通过 Clojure reader 直接读入一个记录类型
  • 一个额外的方便的构造函数,使得我们可以在创建实例的时候添加一些元数据以及一些额外的字段

值语义。值语义意味着两件事情:记录类型是不可变的;如果两个类型的所有对应字段都相等,那么这两个记录本身也是相等的;

记录是一种关系型数据结构。记录类型实现了关系型数据结构的接口,所以那些对 map 操作的函数都可以用在记录类型的实例上。

从记录类型实例去掉一个预定义的字段的话,返回的就不是记录类型了,而是被降级成一个普通的 map。但是如果去除一个运行时额外添加的字段,那么返回的仍然是记录类型,而不会发生降级。

类型

deftype 是 Clojure 里面最底层的定义形式。

实现协议

要让一个给定的类型实现一个协议有两种方法:

  1. 在用 deftype 或者 defrecord 定义类型的时候直接把这些方法实现了,这种方法叫内联实现。

  2. 使用 extend* 系列函数来把一个类型的实现注册到协议上去。

这两种方法的一个细微区别是如何访问字段的值:当在类型之外访问类型的字段时,需要以关键字(或者 Java 互操作方法)来访问。比如 (.x pt),而当内联实现的时候可以直接引用字段名字来获取字段的值,因为内联的时候这些字段在词法范畴里。

内联实现

一般来说内联实现的性能会好一点。有以下两个原因:内联实现的时候能够直接访问类型的字段;内联实现的时候调用协议的方法和 Java 里面调用一个接口的方法一样快。

因为每个协议最终会被编译成一个 Java 的接口,以内联的方式实现一个协议的方法会产生一个实现了协议方法的 Java 类,而那个 Java 类的实现就是对应的协议的方法的实现。

重用实现

类型只能实现协议或者实现接口——没有办法像在其他语言里面一样让一个类型去继承另一个类型,从而继承它的实现。Clojure 对于这个问题的解决办法是利用 extend-type 和 extend-protocol 宏的基础:extend 来重用方法实现。

extend 接受的第一个参数是要进行扩展的类型,然后是要实现的协议的名字以及具体的方法实现的 map,这个 map 里面以方法名作为 key (方法名所对应的关键字),map 的 value 是这个方法的实现。

extenders:返回实现了某个协议的所有类


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

数据分析技术白皮书

数据分析技术白皮书

伍海凤、刘鹏、杨佳静、马师慧Sara、李博、Shirley Song、Zinc、李晓艳 / 2016-8-11 / 0

关于数据分析技术白皮书(Analytics Book 中文版),主要内容围绕: 1. 分析(Analytics):网站分析 & APP分析 2. 谷歌分析工具的原理、部署与使用 3. 开源网站分析工具的原理、部署与使用 4. Log日志分析原理 5. 网站分析的维度与指标定义 6. 如何炼成为一个互联网数据分析师 请访问书的数据分析技术白皮书官网“免费”阅......一起来看看 《数据分析技术白皮书》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换