新媒体
Java工程师就业课程,助你在互联网风口,称霸职场! 5个月让你成为...
北大青鸟辉煌15年 见证我国IT产业发展!今天,北大青鸟毕业学员...
新媒体短视频
华洋视频作品
北大青鸟深圳职业教育,特色专业共建,2024招生简章。
2019年上个与众不同的大学,不仅为学历,更为了学到实用本领
"5年后不会再有互联网公司,因为所有的公司都在用互联网!"李彦宏在...
其他语言(包括 Java 下一代语言)存在巨大的扩展潜力。在本期和接下来的两期文章中,我将探索扩展 Java 类而不涉及继承性的途径。在本文中,您会了解如何向现有类添加方法,无论是直接还是通过语法糖 (syntactic sugar)。
表达式问题
表达式问题是最近的计算机科学历史上的一个众所周知的观察结果,首创于贝尔实验室的 Philip Wadler 的一篇未发表的论文(参见 参考资料)。(Stuart Sierra 在其 developerWorks 文章 “通过 Clojure 1.2 解决表达式问题” 中出色地解释了它。在这篇文章中,Wadler 说道:
表达式问题是老问题的新名字。我们的目标是通过案例定义数据类型,在这里,在不重新编译现有代码的情况下,您可以将新的案例添加到数据类型和数据类型的新函数中,同时保留静态类型安全(例如,没有转换)。
换句话说,您如何向一个分层结构中的类添加功能,而不求助于类型转换或 if 语句?
我们将通过一个简单的例子来表明表达式问题在真实世界中的表现形式。假设您公司始终假设应用程序中的长度单位为米,没有在您的类中为任何其他长度单位构建任何功能。但是,有一天,您公司与一家竞争对手合并了,而这个竞争对手始终假设长度单位为英尺。
解决该问题的一种方法是,通过使用转换方法扩展 Integer,使两种格式之间的切换变得无关紧要。现代语言提供了多种解决方案来实现此目的;在本期中,我将重点介绍其中的 3 种:
开放类包装器类协议回页首Groovy 的类别和 ExpandoMetaClass
Groovy 包含两种使用开放类 扩展现有的类的不同方式,“重新开放” 一个类定义来实现更改(例如添加、更改或删除方法)的能力。
类别类
类别类(一种借鉴自 Objective-C 的概念)是包含静态方法的常规类。每个方法至少接受一个参数,该参数表示方法扩充的类型。如果希望向Integer 添加方法,例如我需要接受该类型作为第一个参数的静态方法,如清单 1 所示:
清单 1. Groovy 的类别类
class IntegerConv {
static Double getAsMeters(Integer self) {
self * 0.30480
}
static Double getAsFeet(Integer self) {
self * 3.2808
}
}
清单 1 中的 IntegerConv 类包含两个扩充方法,每个扩充方法都接受一个名为 self(一个通用的惯用名称)的 Integer 参数。要使用这些方法,我必须将引用代码包装在一个 use 代码块中,如清单 2 所示:
清单 2. 使用类别类
@Test void test_conversion_with_category() {
use(IntegerConv) {
assertEquals(1 * 3.2808, 1.asFeet, 0.1)
assertEquals(1 * 0.30480, 1.asMeters, 0.1)
}
}
清单 2 中有两个特别有趣的地方。首先,尽管 清单 1 中的扩展方法名为 getAsMeters(),但我将它称为 1.asMeters。Groovy 围绕 Java 中的属性的语法糖使我能够执行 getAsMeters() 方法,好像它是名为 asMeters 的类的一个字段一样。如果我在扩展方法中省略了 as,对扩展方法的调用需要使用空括号,就像 1.asMeters() 中一样。一般而言,我喜欢更干净的属性语法,这是编写特定于域的语言 (DSL) 的一种常见技巧。
清单 2 中第二个需要注意的地方是对 asFeet 和 asMeters 的调用。在 use 代码块中,我同等地调用新方法和内置方法。该扩展在 use 代码块的词法范围内是透明的,这很好,因为它限制了扩充(有时是一些核心)类的范围。
ExpandoMetaClass
类别是 Groovy 添加的第一种扩展机制。但事实证明对构建 Grails(基于 Groovy 的 Web 框架)而言,Groovy 的词法范围限制太多了。由于不满类别中的限制,Grails 的创建者之一 Graeme Rocher 向 Groovy 添加了另一种扩展机制:ExpandoMetaClass。
ExpandoMetaClass 是一种懒惰实例化的扩展持有者,它可从任何类 “成长” 而来。清单 3 展示了如何使用 ExpandoMetaClass,为我的Integer 类实现我的扩展:
清单 3. 使用 ExpandoMetaClass 扩展 Integer
class IntegerConvTest{
static {
Integer.metaClass.getAsM { ->
delegate * 0.30480
}
Integer.metaClass.getAsFt { ->
delegate * 3.2808
}
}
@Test void conversion_with_expando() {
assertTrue 1.asM == 0.30480
assertTrue 1.asFt == 3.2808
}
}
在 清单 3 中,我使用 metaClass holder 添加 asM 和 asFt 属性,采用与 清单 2 相同的命名约定。对 metaclass 的调用出现在测试类的一个静态初始化器中,因为我必须确保扩充操作在遇到扩展方法之前发生。
类别类和 ExpandoMetaClass 都在内置方法之前调用扩展类方法。这使您能够添加、更改或删除现有方法。清单 4 给出了一个示例:
清单 4. 取代现有方法的扩展类
@Test void expando_order() {
try {
1.decode()
} catch(NullPointerException ex) {
println("can't decode with no parameters")
}
Integer.metaClass.decode { ->
delegate * Math.PI;
}
assertEquals(1.decode(), Math.PI, 0.1)
}
清单 4 中的第一个 decode() 方法调用是一个内置的静态 Groovy 方法,它设计用于更改整数编码。正常情况下,它会接受一个参数;如果调用时没有任何参数,它将抛出 NullPointerException。但是,当我使用自己的 decode() 方法扩充 Integer 类时,它会取代原始类。
回页首Scala 的隐式转换
Scala 使用包装器类 来解决表达式问题的这个方面。要向一个类添加一个方法,可将它添加到一个帮助类中,然后提供从原始类到您的帮助器的隐式转换。在执行转换之后,您就可以从帮助器隐式地调用该方法,而不是从原始类调用它。清单 5 中的示例使用了这种技术:
清单 5. Scala 的隐式转换
class UnitWrapper(i: Int) {
def asFt = {
i * 3.2808
}
def asM = {
i * 0.30480
}
}
implicit def unitWrapper(i:Int) = new UnitWrapper(i)
println("1 foot = " + 1.asM + " meters");
println("1 meter = " + 1.asFt + "foot")
在 清单 5 中,我定义了一个名为 UnitWrapper 的帮助器类,它接受一个构造函数参数和两个方法:asFt 和 asM。在拥有转换值的帮助类后,我创建了一个 implicit def,实例化一个新的 UnitWrapper。要调用该方法,可以像调用原始类的一个方法那样调用它,比如 1.asM。当 Scala 未在 Integer 类上找到 asM 方法时,它会检查是否存在隐式转换,从而允许将调用类转换为一个包含目标方法的类。像 Groovy 一样,Scala 拥有语法糖,因此我能够省略方法调用的括号,但这是一种语言特性而不是命名约定。
Scala 中的转换帮助器通常是 object 而不是类,但我使用了一个类,因为我希望传递一个值作为构造函数参数(object 不允许这么做)。
Scala 中的隐式转换是一种扩充现有类的精妙且类型安全的方式,但不能向开放类一样,使用这种机制更改或删除现有方法。
回页首Clojure 的协议
Clojure 采用了另一种方法来解决表达式问题的这个方面,那就是结合使用 extend 函数和 Clojure 协议 抽象。协议在概念上类似于一个 Java 接口:一个没有实现的方法签名集合。尽管 Clojure 实质上不是面向对象的,而是偏向于函数,但您可以与类进行交互(并扩展它们),并将方法映射到函数。
为了扩展数字以添加转换,我定义了一个协议,它包含我的两个函数(asF 和 asM)。我可使用该协议 extend 一个现有类(比如Number)。extend 函数接受目标类作为第一个参数,接受该协议作为第二个参数,以及一个使用函数名为键并使用实现(以匿名函数形式)为值的映射。清单 6 显示了 Clojure 单位转换:
清单 6. Clojure 的扩展协议
(defprotocol UnitConversions
(asF [this])
(asM [this]))
(extend Number
UnitConversions
{:asF (fn [this] (* this 3.2808))
:asM #(* % 0.30480)})
我可以在 Clojure REPL(interactive read-eval-print loop,交互式读取-重新运算-打印循环)上使用新的扩展来验证该转换:
user=> (println "1 foot is " (asM 1) " meters")
1 foot is 0.3048 meters
在 清单 6 中,两个转换函数的实现演示了匿名函数声明的两种语法变体。每个函数只接受一个参数(asF 函数中的 this)。单参数函数很常见,以至于 Clojure 为它们的创建提供了语法糖,如 AsM 函数中所示,其中 % 是参数占位符。
协议创建了一种将方法(以函数形式)添加到现有类中的简单解决方案。Clojure 还包含一些有用的宏,使您能够将一组扩展整合在一起。例如,Compojure Web 框架(参见 参考资料)使用协议扩展各种类型,以便它们 “知道” 如何呈现自身。清单 7 显示了来自 Compojure 中的Renderable 的一段代码:
清单 7. 通过协议扩展许多类型
(defprotocol Renderable
(render [this request]
"Render the object into a form suitable for the given request map."))
(extend-protocol Renderable
nil
(render [_ _] nil)
String
(render [body _]
(-> (response body)
(content-type "text/html; charset=utf-8")))
APersistentMap
(render [resp-map _]
(merge (with-meta (response "") (meta resp-map))
resp-map))
IFn
(render [func request]
(render (func request)
; . . .
在 清单 7 中,Renderable 协议是使用单个 render 函数来定义的,该函数接受一个值和一个请求映射作为参数。Clojure 的 extend-protocol宏(它可用于将协议定义分组到一起)接受类型和实现对。在 Clojure 中,您可使用下划线代替不关心的参数。在 清单 7 中,这个定义的可看见部分为 nil、String、APersistentMap 和 IFn(Clojure 中的函数的核心接口)提供了呈现指令。(该框架中还包含其他许多类型,但为节省空间,清单中省略了它们。)可以看到这在实践中非常有用:对于您可能需要呈现的所有类型,您可将语义和扩展放在一起定义。
有很多的学生都有问过这样一个问题,说零基础能学电脑吗?我什么都不会,对电脑一点都不熟悉,我可以学习电脑吗?我可以这么回答你们,零基础是可以学电脑的,而且不用担心学不会,关键是在于你找了一个怎么样的培训学校,北大青鸟东莞金码学校就是一个针对课程针对是零起点的学校。
北大青鸟东莞金码学校各班有兵乓球队和篮球队,学员可以根据自己的兴趣爱好选择球队参加训练及比赛,既锻炼了身体,又能增进同学感情,学员们在宽敞
政府认证+ 国际龙头企业认证+ 大学本科学历 北大青鸟毕业证书介绍北大青鸟证书是针对北大青鸟学员经过北大青鸟各授权培训中心的技能与素
现在这个社会想学电脑的人有很多,因为现在电脑已经和我们的生活息息相关了,也是不可分割的一部分了。是否有很多学生家长就想把孩子送去某一所知名的学校去学电脑呢?那么学电脑到哪里好呢?要说到在哪里学电脑好的话,那要数北大青鸟学校了!
转眼又到了一年一次的高考中考的时候到了,这个夏天你有没有考上你理想的大学或者心仪的高中学校呢?接下来又如何安排你自己的学习路线呢?对于平时成绩不理想的学生来说,是时候该考虑一下,下一步学什么电脑技术对自己的前途有帮助了。那么,学电脑学什么好就业呢?
中国有句古语:好马不吃回头草。在职场上,这句话是否适用呢?未必!于公司而言,虽然春秋国旅的老总曾公开发话:出了春秋国旅的大门,就别再
相信有过求职经历的人都知道,名企的福利待遇相对于比一般的企业要好很多。那么职场新人在面试名企的时候该怎么办呢?今天就和大家分享一下
定期反思自己的职业规划,这是十分重要且必要的。在2013年即将结束之际,也确实应该对事业提出一些值得思考的问题了。仔细思考一下你在事业
大学毕业找工作,你选择了哪个城市作为自己职业生涯的出发点?最近正值毕业求职季,前程无忧论坛(bbs 51job com)用这个话题带网友们回忆了往
很多学生求职难,不是因为素质不好、能力不行、经验不够被刷掉,而是因为简历不够闪!找出写简历的突破口、切入点,才能在HR的筛选过程中脱
本文讲述了oracle数据库实例连接的配置过程,三个重要的配置文件位于C: oracle product 10 1 0 Db_1 NETWORK ADMIN目录下1 sqlplus sys
Activity生命周期 运行、暂停、停止。运行(获得了焦点)、暂停(失去了焦点、但是可见)、停止(失去了焦点、不可见)。中间有可见、不可
2014年04月16日,全球应用网络领导者A10 Networks一举推出四款全新的Thunder?系列设备,进一步丰富延展了其产品线中在中高端的布局。其中,最吸引眼球的当属Thunder 6630 6630S,
phpMyAdmin配置安装全攻略phpMyAdmin安装配置教程傻瓜版,主要为phpMyAdmin 2.11.X 配置安装,其它版本安装方法一样。1、先下载 phpMy...
请大家根据你们的情况一一对号入座。1 装完XP,Vista,Win7后grub无法启动,有Live CD:这种问题是最经常遇到的,要解决问题,你需要一张ubu
7月28日下午,由北大青鸟东莞金码教育举办的校友会圆满落幕。吸引了不少的优秀校友们回校分享交流。20年来,北大青鸟在培养IT技能人才的过
编者按:本文最初是在2010年5月为Fuel Your Coding撰写的,然而该网站现已关闭,为了能让更多人读到,我重新发布了本文。我曾想过针对技
十年前CSDN携手《程序员》杂志首次发起的中国软件开发者大调查活动中,本刊记者用《不安分的2004和震荡的2005》作为调查报道的标题。而十年
面对2017年高考,叵测的未来,很多成绩不理想的考生担心自己的分数线不够该怎么办,其实不上大学,一样可以拥有好前程。毕竟2017年的今天,