系列的过程中我遗漏了 Scala 的语言的一个重要方面:Scala 的包和访问修饰符功能。所以在研究该语言的函数性元素 apply 机制前,我将先介绍包和访问修饰符。
打包
为了有助于隔离代码,使其不会相互冲突,Java™ 代码提供了 package 关键词,由此创建了一个词法命名空间,用以声明类。本质上,将类 Foo 放置到名为 com.tedneward.util 包中就将正式类名修改成了 com.tedneward.util.Foo;同理,必须按该方法引用类。如果没有,Java 编程人员会很快指出,他们会 import 该包,避免键入正式名的麻烦。的确如此,但这仅意味着根据正式名引用类的工作由编译器和字节码完成。快速浏览一下 javap 的输出,这点就会很明了。
关于本系列Ted Neward 将和您一起深入探讨 Scala 编程语言。在这个新的 developerWorks 系列 中,您将深入了解 Sacla,并在实践中看到 Scala 的语言功能。进行比较时,Scala 代码和 Java 代码将放在一起展示,但(您将发现)Scala 中的许多内容与您在 Java 编程中发现的任何内容都没有直接关联,而这正是 Scala 的魅力所在!如果用 Java 代码就能够实现的话,又何必再学习 Scala 呢?
.然而,Java 语言中的包还有几个特殊的要求:一定要在包所作用的类所在的 .java 文件的顶端声明包(在将注释应用于包时,这一点会引发很严重的语言问题);该声明的作用域为整个文件。这意味着两个跨包进行紧密耦合的类一定要在跨文件时分离,这会致使两者间的紧密耦合很容易被忽略。
Scala 在打包方面所采取的方法有些不同,它结合使用了 Java 语言的 declaration 方法和 C# 的 scope(限定作用域)方法。了解了这一点,Java 开发人员就可以使用传统的 Java 方法并将 package 声明放在 .scala 文件的顶部,就像普通的 Java 类一样;包声明的作用域为整个文件,就像在 Java 代码中一样。而 Scala 开发人员则可以使用 Scala 的包 “(scoping)限定作用域” 方法,用大括号限制 package 语句的作用域,如清单 1 所示:
清单 1. 简化的打包
这个代码有效地声明了类 App,或者更确切的说是一个称为 com.tedneward.scala.demonstration.App 的单个类。注意 Scala 还允许用点分隔包名,所以清单 1 中的代码可以更简洁,如清单 2 所示:
清单 2. 简化了的打包(redux)
用哪一种样式看起来都比较合适,因为它们都编译出一样的代码构造(Scala 将继续编译并和 javac 一样在声明包的子目录中生成 .class 文件)。
导入
与包相对的当然就是 import 了,Scala 使用它将名称放入当前词法名称空间。本系列的读者已经在此前的很多例子中见到过 import 了,但现在我将指出一些让 Java 开发人员大吃一惊的 import 的特性。
首先,import 可以用于客户机 Scala 文件内的任何地方,并非只可以用在文件的顶部,这样就有了作用域的关联性。因此,在清单 3 中,java.math.BigInteger 导入的作用域被完全限定到了在 App 对象内部定义的方法,其他地方都不行。如果 mathfun 内的其他类或对象要想使用 java.math.BigInteger,就需要像 App 一样导入该类。如果 mathfun 的几个类都想使用 java.math.BigInteger,可以在 App 的定义以外的包级别导入该类,这样在包作用域内的所有类就都导入 BigInteger 了。
清单 3. 导入的作用域
不只如此,Scala 还不区分高层成员和嵌套成员,所以您不仅可以使用 import 将嵌套类型的成员置于词法作用域中,其他任何成员均可;例如,您可以通过导入 java.math.BigInteger 内的所有名称,使对 ZERO 和 ONE 的限定了作用域的引用缩小为清单 4 中的名称引用:
清单 4. 静态导入
您可以使用下划线(还记得 Scala 中的通配符吧?)有效地告知 Scala 编译器 BigInteger 内的所有成员都需要置入作用域。由于 BigInteger 已经被先前的导入语句导入到作用域中,因此无需显式地使用包名限定类名。实际上,可以将所有这些都结合到一个语句中,因为 import 可以同时导入多个目标,目标间用逗号隔开(如清单 5 所示):
清单 5. 批量导入
这样您可以节省一两行代码。注意这两个导入过程不能结合:先导入 BigInteger 类本身,再导入该类中的各种成员。
也可以使用 import 来引入其他非常量的成员。例如,考虑一下清单 6 中的数学工具库(或许不一定有什么价值):
清单 6. Enron 的记帐代码
使用这个库会越来越觉得麻烦,因为每请求它的一个成员,都需要键入 BizarroMath,但是 Scala 允许将 BizarroMath 的每一个成员导入最高层的词法空间,因此简直就可以把它们当成全局函数来使用(如清单 7所示):
清单 7. 计算 Enron的开支
还有其他的一些构造很有趣,它们允许 Scala 开发人员写出更自然的 2 bizplus 2,但是这些内容本文不予讨论(想了解 Scala 潜在的可以用于其他用途的特性的读者可以看一下 Odersky、Spoon 和 Venners 所著的 Programming in Scala 中谈到的 Scala implicit 构造)。
访问
打包(和导入)是 Scala 封装的一部分,和在 Java 代码中一样,在 Scala 中,打包很大一部分在于以选择性方式限定访问特定成员的能力 — 换句话说,在于 Scala 将特定成员标记为 “公有(public)”、“private(私有)” 或介于两者之间的成员的能力。
Java 语言有四个级别的访问:公有(public)、私有(private)、受保护的(protected )和包级别(它没有任何关键词)访问。Scala:
•废除了包级别的限制(在某种程度上)
•默认使用 “公有”
•指定 “私有” 表示 “只有此作用域可访问”
相反,Scala 定义 “protected” 的方式与在 Java 代码中不同;Java protected 成员对于子类和在其中定义成员的包来说是可访问的,Scala 中则仅有子类可访问。这意味着 Scala 版本的 protected 限制性要比 Java 版本更严格(虽然按理说更加直观)。
然而,Scala 真正区别于 Java 代码的地方是 Scala 中的访问修饰符可以用包名来 “限定”,用以表明直到 哪个访问级别才可以访问成员。例如,如果 BizarroMath 包要将成员访问权限授权给同一包中的其他成员(但不包括子类),可以用清单 8 中的代码来实现:
清单 8. Enron 的记帐代码
注意此处的 private[mathfun] 表达。本质上,这里的访问修饰符是说该成员直到 包 mathfun 为止都是私有的;这意味着包 mathfun 的任何成员都有权访问 bizexp,但任何包以外的成员都无权访问它,包括子类。
这一点的强大意义就在于任何包都可以使用 “private” 或者 “protected” 声明甚至 com(乃至 _root_,它是根名称空间的别名,因此本质上 private[_root_] 等效于 “public” 同)进行声明。这使得 Scala 能够为访问规范提供一定程度的灵活性,远远高于 Java 语言所提供的灵活性。
实际上,Scala 提供了一个更高程度的访问规范:对象私有 规范,用 private[this] 表示,它规定只有被同一对象调用的成员可以访问有关成员,其他对象里的成员都不可以,即使对象的类型相同(这弥合了 Java 访问规范系统中的一个缺口,这个缺口除对 Java 编程问题有用外,别无他用。)
注意访问修饰符必须在某种程度上在 JVM 之上映射,这致使定义中的细枝末节会在从正规 Java 代码中调用或编译时丢失。例如,上面的 BizarroMath 示例(用 private[mathfun] 声明的成员 bizexp)将会生成清单 9 中的类定义(当用 javap 来查看时):
Listing 9. Enron 的记帐库,JVM 视图
在编译的 BizarroMath 类的第二行很容易看出,bizexp() 方法被赋予了 JVM 级别的 public 访问修饰符,这意味着一旦 Scala 编译器结束访问检查,细微的 private[mathfun] 区别就会丢失。因此,对于那些要从 Java 代码使用的 Scala 代码,我宁愿坚持传统的 “private” 和 “public” 的定义(甚至 “protected” 的定义有时最终映射到 JVM 级别的 “public”,所有不确定的时候,请对照实际编译的字节码参考一下 javap,以确认其访问级别。)
应用
在本系列上一期的文章中(“集合类型”),当谈及 Scala 中的数组时(确切地说是 Array[T])我说过:“获取数组的第 i 个元素” 实际上是 “那些名称很有趣的方法中的一种……”。尽管当时是因为我不想深入细节,但不管怎么说事实证明这种说法严格来说 是不对的。
好吧,我承认,我说谎了。
技术上讲,在 Array[T] 类上使用圆括号要比使用 “名称有趣的方法” 复杂一点;Scala 为特殊的字符序列(即那些有左右括号的序列)保留了一个特殊名称关联,因为它有着特殊的使用意图 :“做”……(或按函数来说,将……“应用” 到……)。
换句话说,Scala 有一个特殊的语法(更确切一些,是一个特殊的语法关系)来代替 “应用” 操作符 “()”。更精确地说,当用 () 作为方法调用来调用所述对象时,Scala 将称为 apply() 的方法作为调用的方法。例如,一个想充当仿函数(functor)的类(一个充当函数的对象)可以定义一个 apply 方法来提供类似于函数或方法的语义:
清单 10. 使用 Functor!
好奇的读者会想是什么使仿函数不同于匿名函数或闭包呢?事实证明,它们之间的关系相当明显:标准 Scala 库中的 Function1 类型(指包含一个参数的函数)在其定义上有一个 apply 方法。快速浏览一些为 Scala 匿名函数生成的 Scala 匿名类,您就会明白生成的类是 Function1(或者 Function2 或 Function3,这要看该函数使用了几个参数)的后代。
这意味着当匿名的或者命名的函数不一定适合期望设计方法时,Scala 开发人员可以创建一个 functor 类,提供给它一些初始化数据,保存在字段中,然后通过 () 执行它,无需任何通用基类(传统的策略模式实现需要这个类):
清单 11. 使用 Functor!
任何提供了被适当赋予了参数的 apply 方法的类,只要这些参数都按数字和类型排列了起来,它们都会在被调用时运行。
结束语
Scala 的打包、导入和访问修饰符机制提供了传统 Java 编程人员从未享受过的更高级的控制和封装。例如,它们提供了导入一个对象的选择方法的能力,使它们看起来就像全局方法一样,而且还克服了全局方法的传统的缺点;它们使得使用那些方法变得极其简单,尤其是当这些方法提供了诸如本系列早期文章(“Scala 控制结构内部揭密”)引入的虚构的 tryWithLogging 函数这样的高级功能时。
同样,“应用” 机制允许 Scala 隐藏函数部分的执行细节,这样,编程人员可能会不知道(或不在乎)他们正调用的东西 事实上不是一个函数,而是一个非常复杂的对象。该机制为 Scala 机制的函数特性提供了另一个方面,当然 Java 语言(或者 C# 或 C++)也提供了这个方面,但是它们提供的语法纯度没有 Scala 的高。
这就是本期的全部内容;在下一期发布前,请尽情欣赏!
打包
为了有助于隔离代码,使其不会相互冲突,Java™ 代码提供了 package 关键词,由此创建了一个词法命名空间,用以声明类。本质上,将类 Foo 放置到名为 com.tedneward.util 包中就将正式类名修改成了 com.tedneward.util.Foo;同理,必须按该方法引用类。如果没有,Java 编程人员会很快指出,他们会 import 该包,避免键入正式名的麻烦。的确如此,但这仅意味着根据正式名引用类的工作由编译器和字节码完成。快速浏览一下 javap 的输出,这点就会很明了。
关于本系列Ted Neward 将和您一起深入探讨 Scala 编程语言。在这个新的 developerWorks 系列 中,您将深入了解 Sacla,并在实践中看到 Scala 的语言功能。进行比较时,Scala 代码和 Java 代码将放在一起展示,但(您将发现)Scala 中的许多内容与您在 Java 编程中发现的任何内容都没有直接关联,而这正是 Scala 的魅力所在!如果用 Java 代码就能够实现的话,又何必再学习 Scala 呢?
.然而,Java 语言中的包还有几个特殊的要求:一定要在包所作用的类所在的 .java 文件的顶端声明包(在将注释应用于包时,这一点会引发很严重的语言问题);该声明的作用域为整个文件。这意味着两个跨包进行紧密耦合的类一定要在跨文件时分离,这会致使两者间的紧密耦合很容易被忽略。
Scala 在打包方面所采取的方法有些不同,它结合使用了 Java 语言的 declaration 方法和 C# 的 scope(限定作用域)方法。了解了这一点,Java 开发人员就可以使用传统的 Java 方法并将 package 声明放在 .scala 文件的顶部,就像普通的 Java 类一样;包声明的作用域为整个文件,就像在 Java 代码中一样。而 Scala 开发人员则可以使用 Scala 的包 “(scoping)限定作用域” 方法,用大括号限制 package 语句的作用域,如清单 1 所示:
清单 1. 简化的打包
package com { package tedneward { package scala { package demonstration { object App { def main(args : Array[String]) : Unit = { System.out.println("Howdy, from packaged code!") args.foreach((i) => System.out.println("Got " + i) ) } } } } } }
这个代码有效地声明了类 App,或者更确切的说是一个称为 com.tedneward.scala.demonstration.App 的单个类。注意 Scala 还允许用点分隔包名,所以清单 1 中的代码可以更简洁,如清单 2 所示:
清单 2. 简化了的打包(redux)
package com.tedneward.scala.demonstration { object App { def main(args : Array[String]) : Unit = { System.out.println("Howdy, from packaged code!") args.foreach((i) => System.out.println("Got " + i) ) } } }
用哪一种样式看起来都比较合适,因为它们都编译出一样的代码构造(Scala 将继续编译并和 javac 一样在声明包的子目录中生成 .class 文件)。
导入
与包相对的当然就是 import 了,Scala 使用它将名称放入当前词法名称空间。本系列的读者已经在此前的很多例子中见到过 import 了,但现在我将指出一些让 Java 开发人员大吃一惊的 import 的特性。
首先,import 可以用于客户机 Scala 文件内的任何地方,并非只可以用在文件的顶部,这样就有了作用域的关联性。因此,在清单 3 中,java.math.BigInteger 导入的作用域被完全限定到了在 App 对象内部定义的方法,其他地方都不行。如果 mathfun 内的其他类或对象要想使用 java.math.BigInteger,就需要像 App 一样导入该类。如果 mathfun 的几个类都想使用 java.math.BigInteger,可以在 App 的定义以外的包级别导入该类,这样在包作用域内的所有类就都导入 BigInteger 了。
清单 3. 导入的作用域
package com { package tedneward { package scala { // ... package mathfun { object App { import java.math.BigInteger def factorial(arg : BigInteger) : BigInteger = { if (arg == BigInteger.ZERO) BigInteger.ONE else arg multiply (factorial (arg subtract BigInteger.ONE)) } def main(args : Array[String]) : Unit = { if (args.length > 0) System.out.println("factorial " + args(0) + " = " + factorial(new BigInteger(args(0)))) else System.out.println("factorial 0 = 1") } } } } } }
不只如此,Scala 还不区分高层成员和嵌套成员,所以您不仅可以使用 import 将嵌套类型的成员置于词法作用域中,其他任何成员均可;例如,您可以通过导入 java.math.BigInteger 内的所有名称,使对 ZERO 和 ONE 的限定了作用域的引用缩小为清单 4 中的名称引用:
清单 4. 静态导入
package com { package tedneward { package scala { // ... package mathfun { object App { import java.math.BigInteger import BigInteger._ def factorial(arg : BigInteger) : BigInteger = { if (arg == ZERO) ONE else arg multiply (factorial (arg subtract ONE)) } def main(args : Array[String]) : Unit = { if (args.length > 0) System.out.println("factorial " + args(0) + " = " + factorial(new BigInteger(args(0)))) else System.out.println("factorial 0 = 1") } } } } } }
您可以使用下划线(还记得 Scala 中的通配符吧?)有效地告知 Scala 编译器 BigInteger 内的所有成员都需要置入作用域。由于 BigInteger 已经被先前的导入语句导入到作用域中,因此无需显式地使用包名限定类名。实际上,可以将所有这些都结合到一个语句中,因为 import 可以同时导入多个目标,目标间用逗号隔开(如清单 5 所示):
清单 5. 批量导入
package com { package tedneward { package scala { // ... package mathfun { object App { import java.math.BigInteger, BigInteger._ def factorial(arg : BigInteger) : BigInteger = { if (arg == ZERO) ONE else arg multiply (factorial (arg subtract ONE)) } def main(args : Array[String]) : Unit = { if (args.length > 0) System.out.println("factorial " + args(0) + " = " + factorial(new BigInteger(args(0)))) else System.out.println("factorial 0 = 1") } } } } } }
这样您可以节省一两行代码。注意这两个导入过程不能结合:先导入 BigInteger 类本身,再导入该类中的各种成员。
也可以使用 import 来引入其他非常量的成员。例如,考虑一下清单 6 中的数学工具库(或许不一定有什么价值):
清单 6. Enron 的记帐代码
package com { package tedneward { package scala { // ... package mathfun { object BizarroMath { def bizplus(a : Int, b : Int) = { a - b } def bizminus(a : Int, b : Int) = { a + b } def bizmultiply(a : Int, b : Int) = { a / b } def bizdivide(a : Int, b : Int) = { a * b } } } } } }
使用这个库会越来越觉得麻烦,因为每请求它的一个成员,都需要键入 BizarroMath,但是 Scala 允许将 BizarroMath 的每一个成员导入最高层的词法空间,因此简直就可以把它们当成全局函数来使用(如清单 7所示):
清单 7. 计算 Enron的开支
package com { package tedneward { package scala { package demonstration { object App2 { def main(args : Array[String]) : Unit = { import com.tedneward.scala.mathfun.BizarroMath._ System.out.println("2 + 2 = " + bizplus(2,2)) } } } } } }
还有其他的一些构造很有趣,它们允许 Scala 开发人员写出更自然的 2 bizplus 2,但是这些内容本文不予讨论(想了解 Scala 潜在的可以用于其他用途的特性的读者可以看一下 Odersky、Spoon 和 Venners 所著的 Programming in Scala 中谈到的 Scala implicit 构造)。
访问
打包(和导入)是 Scala 封装的一部分,和在 Java 代码中一样,在 Scala 中,打包很大一部分在于以选择性方式限定访问特定成员的能力 — 换句话说,在于 Scala 将特定成员标记为 “公有(public)”、“private(私有)” 或介于两者之间的成员的能力。
Java 语言有四个级别的访问:公有(public)、私有(private)、受保护的(protected )和包级别(它没有任何关键词)访问。Scala:
•废除了包级别的限制(在某种程度上)
•默认使用 “公有”
•指定 “私有” 表示 “只有此作用域可访问”
相反,Scala 定义 “protected” 的方式与在 Java 代码中不同;Java protected 成员对于子类和在其中定义成员的包来说是可访问的,Scala 中则仅有子类可访问。这意味着 Scala 版本的 protected 限制性要比 Java 版本更严格(虽然按理说更加直观)。
然而,Scala 真正区别于 Java 代码的地方是 Scala 中的访问修饰符可以用包名来 “限定”,用以表明直到 哪个访问级别才可以访问成员。例如,如果 BizarroMath 包要将成员访问权限授权给同一包中的其他成员(但不包括子类),可以用清单 8 中的代码来实现:
清单 8. Enron 的记帐代码
package com { package tedneward { package scala { // ... package mathfun { object BizarroMath { def bizplus(a : Int, b : Int) = { a - b } def bizminus(a : Int, b : Int) = { a + b } def bizmultiply(a : Int, b : Int) = { a / b } def bizdivide(a : Int, b : Int) = { a * b } private[mathfun] def bizexp(a : Int, b: Int) = 0 } } } } }
注意此处的 private[mathfun] 表达。本质上,这里的访问修饰符是说该成员直到 包 mathfun 为止都是私有的;这意味着包 mathfun 的任何成员都有权访问 bizexp,但任何包以外的成员都无权访问它,包括子类。
这一点的强大意义就在于任何包都可以使用 “private” 或者 “protected” 声明甚至 com(乃至 _root_,它是根名称空间的别名,因此本质上 private[_root_] 等效于 “public” 同)进行声明。这使得 Scala 能够为访问规范提供一定程度的灵活性,远远高于 Java 语言所提供的灵活性。
实际上,Scala 提供了一个更高程度的访问规范:对象私有 规范,用 private[this] 表示,它规定只有被同一对象调用的成员可以访问有关成员,其他对象里的成员都不可以,即使对象的类型相同(这弥合了 Java 访问规范系统中的一个缺口,这个缺口除对 Java 编程问题有用外,别无他用。)
注意访问修饰符必须在某种程度上在 JVM 之上映射,这致使定义中的细枝末节会在从正规 Java 代码中调用或编译时丢失。例如,上面的 BizarroMath 示例(用 private[mathfun] 声明的成员 bizexp)将会生成清单 9 中的类定义(当用 javap 来查看时):
Listing 9. Enron 的记帐库,JVM 视图
Compiled from "packaging.scala" public final class com.tedneward.scala.mathfun.BizarroMath extends java.lang.Object { public static final int $tag(); public static final int bizexp(int, int); public static final int bizdivide(int, int); public static final int bizmultiply(int, int); public static final int bizminus(int, int); public static final int bizplus(int, int); }
在编译的 BizarroMath 类的第二行很容易看出,bizexp() 方法被赋予了 JVM 级别的 public 访问修饰符,这意味着一旦 Scala 编译器结束访问检查,细微的 private[mathfun] 区别就会丢失。因此,对于那些要从 Java 代码使用的 Scala 代码,我宁愿坚持传统的 “private” 和 “public” 的定义(甚至 “protected” 的定义有时最终映射到 JVM 级别的 “public”,所有不确定的时候,请对照实际编译的字节码参考一下 javap,以确认其访问级别。)
应用
在本系列上一期的文章中(“集合类型”),当谈及 Scala 中的数组时(确切地说是 Array[T])我说过:“获取数组的第 i 个元素” 实际上是 “那些名称很有趣的方法中的一种……”。尽管当时是因为我不想深入细节,但不管怎么说事实证明这种说法严格来说 是不对的。
好吧,我承认,我说谎了。
技术上讲,在 Array[T] 类上使用圆括号要比使用 “名称有趣的方法” 复杂一点;Scala 为特殊的字符序列(即那些有左右括号的序列)保留了一个特殊名称关联,因为它有着特殊的使用意图 :“做”……(或按函数来说,将……“应用” 到……)。
换句话说,Scala 有一个特殊的语法(更确切一些,是一个特殊的语法关系)来代替 “应用” 操作符 “()”。更精确地说,当用 () 作为方法调用来调用所述对象时,Scala 将称为 apply() 的方法作为调用的方法。例如,一个想充当仿函数(functor)的类(一个充当函数的对象)可以定义一个 apply 方法来提供类似于函数或方法的语义:
清单 10. 使用 Functor!
class ApplyTest { import org.junit._, Assert._ @Test def simpleApply = { class Functor { def apply() : String = { "Doing something without arguments" } def apply(i : Int) : String = { if (i == 0) "Done" else "Applying... " + apply(i - 1) } } val f = new Functor assertEquals("Doing something without arguments", f() ) assertEquals("Applying... Applying... Applying... Done", f(3)) } }
好奇的读者会想是什么使仿函数不同于匿名函数或闭包呢?事实证明,它们之间的关系相当明显:标准 Scala 库中的 Function1 类型(指包含一个参数的函数)在其定义上有一个 apply 方法。快速浏览一些为 Scala 匿名函数生成的 Scala 匿名类,您就会明白生成的类是 Function1(或者 Function2 或 Function3,这要看该函数使用了几个参数)的后代。
这意味着当匿名的或者命名的函数不一定适合期望设计方法时,Scala 开发人员可以创建一个 functor 类,提供给它一些初始化数据,保存在字段中,然后通过 () 执行它,无需任何通用基类(传统的策略模式实现需要这个类):
清单 11. 使用 Functor!
class ApplyTest { import org.junit._, Assert._ // ... @Test def functorStrategy = { class GoodAdder { def apply(lhs : Int, rhs : Int) : Int = lhs + rhs } class BadAdder(inflateResults : Int) { def apply(lhs : Int, rhs : Int) : Int = lhs + rhs * inflateResults } val calculator = new GoodAdder assertEquals(4, calculator(2, 2)) val enronAccountant = new BadAdder(50) assertEquals(102, enronAccountant(2, 2)) } }
任何提供了被适当赋予了参数的 apply 方法的类,只要这些参数都按数字和类型排列了起来,它们都会在被调用时运行。
结束语
Scala 的打包、导入和访问修饰符机制提供了传统 Java 编程人员从未享受过的更高级的控制和封装。例如,它们提供了导入一个对象的选择方法的能力,使它们看起来就像全局方法一样,而且还克服了全局方法的传统的缺点;它们使得使用那些方法变得极其简单,尤其是当这些方法提供了诸如本系列早期文章(“Scala 控制结构内部揭密”)引入的虚构的 tryWithLogging 函数这样的高级功能时。
同样,“应用” 机制允许 Scala 隐藏函数部分的执行细节,这样,编程人员可能会不知道(或不在乎)他们正调用的东西 事实上不是一个函数,而是一个非常复杂的对象。该机制为 Scala 机制的函数特性提供了另一个方面,当然 Java 语言(或者 C# 或 C++)也提供了这个方面,但是它们提供的语法纯度没有 Scala 的高。
这就是本期的全部内容;在下一期发布前,请尽情欣赏!
发表评论
-
Scala + Twitter = Scitter(scala代码学习第15天)
2011-04-08 09:11 865Twitter 迅速占领了 Interne ... -
面向 Java 开发人员的 Scala 指南: Scala 和 servlet(scala代码学习第十一天)
2011-04-02 07:40 732Scala 显然是一门有趣的语言,很适合体现语言理论和创新方面 ... -
构建计算器,第 3 部分将 Scala 解析器组合子和 case 类结合起来(scala代码学习第十天)
2011-04-01 09:25 949欢迎勇于探索的读者回到我们的系列文章中!本月继续探索 Scal ... -
scala代码学习构建计算器,第2 部分(代码学习第九天)
2011-03-31 10:53 806回忆一下我们的英雄所处的困境:在试图创建一个 DSL(这里只不 ... -
Scala构建计算器,第1 部分(代码学习第8天)
2011-03-30 11:59 1192特定于领域的语言 可能您无法(或没有时间)承受来自于您的项目 ... -
实现继承(代码学习第五天)
2011-03-26 10:13 963近十几年来,面向对象语言设计的要素一直是继承的核心。不支持继承 ... -
关于特征和行为(代码学习第四天)
2011-03-25 09:38 697著名科学家、研究学者 ... -
Scala 控制结构内部揭密(scala代码学习第三天)
2011-03-24 09:15 1304迄今为止,在此 系列 ... -
面向 Java 开发人员的 Scala 指南: 类操作(代码学习第2天)
2011-03-22 19:06 740第一天中只是些简单应用 ,您只是稍微了解了一些 Scala 语 ... -
programming in scala 2nd代码学习(第一天)
2011-03-22 18:42 932近来没事,拿出了原先学习scala的代码 书中代码噢、拿出自己 ... -
scalatra web框架快速搭建(官方使用文档)
2011-03-21 22:42 2513昨天写了个sbt构建scala项目的文章,就是为了今天的sca ... -
A build tool for Scala(simple-build-tool) sbt安装指南
2011-03-20 22:49 2200今天有位写框架的大哥叫我学一学scalatra框架,找了 ... -
Scala functional style deferent from java OOP(特点)
2011-03-20 17:34 981该程序通过一段斐波那契数列的计算,比较一下Scala的函数式编 ... -
Java 开发人员的 Scala 指南: 面向对象的函数编程
2011-03-20 11:59 1034函数概念 开始之前, ...
相关推荐
Scala是一种强大的多范式编程语言,它融合了面向对象和函数式编程的特性。...这个"scala学习源代码"的压缩包是学习Scala的宝贵资源,通过阅读和实践其中的代码,你将能够深入理解Scala的精髓并提升你的编程技能。
Scala 语言中的访问修饰符是控制类、对象、方法和变量访问权限的关键工具,它们确保了代码的封装性和安全性。Scala 提供了与 Java 类似的访问修饰符,但有些细节有所不同,这使得 Scala 的访问控制更为严格。 1. **...
这个压缩包文件"prog-scala-2nd-ed-code-examples-master"包含了Scala程序设计的实例源代码,是学习和理解Scala编程语言的宝贵资源。 在"Scala程序设计 例子 源代码"这个标题中,我们可以推测出这份资料可能来自一...
和Java一样,scala也可以通过访问修饰符,来控制成员变量和成员方法是否可以被访问。 定义 Java中的访问控制,同样适用于scala,可以在成员前面添加private/protected关键字来控制成员的可见性。但在scala中,没有...
Scala3通过`trait`的`sealed`修饰符和`object`的`opaque`修饰符来鼓励更严格的封装。`sealed`特质限制了其子类只能在定义的文件或模块内存在,`opaque`对象则使得类型可以有私有的实现细节,对外只暴露必要的接口。 ...
Scala是一种强大的多范式编程语言,它融合了面向对象和函数式编程的特性,被广泛应用于大数据处理、分布式计算和Web开发等领域。Spark是基于Scala构建的大数据处理框架,其高性能和易用性使得Scala在大数据领域备受...
Scala和Spark是大数据分析领域中的两个重要工具,它们在处理大规模数据时表现出强大的性能和灵活性。Scala是一种静态类型的函数式编程语言,而Spark是一个分布式计算框架,尤其适合于大数据处理和分析。本教程将深入...
在Scala编程中,使用正确的库和框架(通常以JAR包的形式)是至关重要的,因为它们提供了丰富的功能,可以帮助开发者更高效地工作。"scala 本地架包"指的是将Scala库或者框架保存在本地文件系统中,以便于项目中引用...
本课件是针对Scala学习者精心准备的资源,旨在帮助你深入理解和掌握Scala的核心概念,并进一步熟悉在Spark框架中的应用。 首先,我们从"Scala进阶之路-part01-基础.pdf"开始,这部分内容主要涵盖了Scala的基础知识...
7. **类型推断**:Scala的类型推断使得编写简洁的代码成为可能,编译器会自动推断变量和函数的类型。查看源码可以理解这个过程。 8. **Dotty和Scala 3的影响**:虽然Scala 2.12.8是基于旧的语法和语义,但它也受到...
Scala是一种强大的多范式编程语言,它融合了面向对象和函数式编程的概念。这个"scala2.12.1Windows镜像包"是为Windows操作系统设计的Scala编程环境的安装包,版本号为2.12.1。Scala 2.12.x系列是其重要的一个稳定...
"快学Scala第二版本示例代码" 提供了一种系统性的学习途径,帮助开发者深入理解Scala的核心概念和实践应用。 首先,从文件名列表来看,我们可以看到一系列按照章节组织的代码示例,比如`ch20`到`ch03`。这暗示了...
Scala是Apache Spark的主要编程语言,它为Spark提供了强大的面向对象编程模型,使得处理大规模数据变得更加高效和便捷。Spark作为一个分布式计算框架,包含了四个主要模块:Spark Core、Spark SQL、Spark Streaming...
通过学习这些代码清单,读者不仅可以深入理解Scala语言本身,还能掌握如何利用Scala构建实际的、高效的应用程序,特别是当涉及到响应式编程和构建工具的使用时,能更好地适应现代软件开发的需求。
7. **类型系统**:Scala拥有强大的类型系统,包括类型推断、模式匹配、高阶类型和抽象类型等,这使得编写安全且可维护的代码成为可能。 8. **面向对象编程**:Scala支持传统的类、继承、封装和多态性,同时也引入了...
通过阅读《Programming in Scala》第三版并实践提供的源代码,学习者将能够掌握Scala的核心概念,包括其面向对象和函数式编程的融合、强大的类型系统、模式匹配以及并发处理能力。这将为开发者打开新的编程视角,...
Scala的学习曲线相对较陡,但一旦掌握,可以极大地提高开发效率和代码质量。 Scala深入学习的资料通常会覆盖如下知识点: 1. Scala基础知识:这包括Scala的基本语法、数据类型、控制结构等。例如,"Getting ...
这个打包文件中包含了《SCALA程序设计-JAVA虚拟机多核编程实战》《Scala编程-中文-完整版》《Scala in Action》三本书,足以让你从scala入门到精通,让我们一起愉快的学习吧。spark,scala醉了醉了。哈哈
【标题】"flinkDemo完整代码scala版"指的是一个基于Scala实现的Apache Flink示例项目,该项目包含了对Flink核心功能的全面...这些示例代码是学习和提升Flink技能的重要资源,尤其对于那些熟悉Scala语言的开发者来说。
5. 编写和运行代码:使用Scala编写Spark程序,然后通过ECLIPSE的运行配置来启动Spark集群并提交作业。 总之,Spark Scala开发依赖包对于在ECLIPSE中进行大数据处理项目至关重要。正确配置这些依赖将使开发者能够在...