第一天中只是些简单应用 ,您只是稍微了解了一些 Scala 语法,这些是运行 Scala 程序和了解其简单特性的最基本要求。通过上一篇文章中的 Hello World 和 Timer 示例程序,您了解了 Scala 的 Application 类、方法定义和匿名函数的语法,还稍微了解了 Array[] 和一些类型推断方面的知识。Scala 还提供了很多其他特性,本文将研究 Scala 编程中的一些较复杂方面。
Scala 的函数编程特性非常引人注目,但这并非 Java 开发人员应该对这门语言感兴趣的惟一原因。实际上,Scala 融合了函数概念和面向对象概念。为了让 Java 和 Scala 程序员感到得心应手,可以了解一下 Scala 的对象特性,看看它们是如何在语言方面与 Java 对应的。记住,其中的一些特性并不是直接对应,或者说,在某些情况下,“对应” 更像是一种类比,而不是直接的对应。不过,遇到重要区别时,我会指出来。
Scala 和 Java 一样使用类
我们不对 Scala 支持的类特性作冗长而抽象的讨论,而是着眼于一个类的定义,这个类可用于为 Scala 平台引入对有理数的支持(主要借鉴自 “Scala By Example”,参见 参考资料):
从词汇上看,清单 1 的整体结构与 Java 代码类似,但是,这里显然还有一些新的元素。在详细讨论这个定义之前,先看一段使用这个新 Rational 类的代码:
清单 2 中的内容平淡无奇:先创建两个有理数,然后再创建两个 Rational,作为前面两个有理数的和与差,最后将这几个数回传到控制台上(注意, Console.println() 来自 Scala 核心库,位于 scala.* 中,它被隐式地导入每个 Scala 程序中,就像 Java 编程中的 java.lang 一样)。
--------------------------------------------------------------------------------
回页首
用多少种方法构造类?
现在,回顾一下 Rational 类定义中的第一行:
您也许会认为清单 3 中使用了某种类似于泛型的语法,这其实是 Rational 类的默认的、首选的构造函数:n 和 d 是构造函数的参数。
Scala 优先使用单个构造函数,这具有一定的意义 —— 大多数类只有一个构造函数,或者通过一个构造函数将一组构造函数 “链接” 起来。如果需要,可以在一个 Rational 上定义更多的构造函数,例如:
注意,Scala 的构造函数链通过调用首选构造函数(Int,Int 版本)实现 Java 构造函数链的功能。
实现细节
在处理有理数时,采取一点数值技巧将会有所帮助:也就是说,找到公分母,使某些操作变得更容易。如果要将 1/2 与 2/4 相加,那么 Rational 类应该足够聪明,能够认识到 2/4 和 1/2 是相等的,并在将这两个数相加之前进行相应的转换。
嵌套的私有 gcd() 函数和 Rational 类中的 g 值可以实现这样的功能。在 Scala 中调用构造函数时,将对整个类进行计算,这意味着将 g 初始化为 n 和 d 的最大公分母,然后用它依次设置 n 和 d。
回顾一下 清单 1 就会发现,我创建了一个覆盖的 toString 方法来返回 Rational 的值,在 RunRational 驱动程序代码中使用 toString 时,这样做非常有用。
然而,请注意 toString 的语法:定义前面的 override 关键字是必需的,这样 Scala 才能确认基类中存在相应的定义。这有助于预防因意外的输入错误导致难于觉察的 bug(Java 5 中创建 @Override 注释的动机也在于此)。还应注意,这里没有指定返回类型 —— 从方法体的定义很容易看出 —— 返回值没有用 return 关键字显式地标注,而在 Java 中则必须这样做。相反,函数中的最后一个值将被隐式地当作返回值(但是,如果您更喜欢 Java 语法,也可以使用 return 关键字)。
--------------------------------------------------------------------------------
回页首
一些重要值
接下来分别是 numer 和 denom 的定义。这里涉及的语法可能让 Java 程序员认为 numer 和 denom 是公共的 Int 字段,它们分别被初始化为 n-over-g 和 d-over-g;但这种想法是不对的。
在形式上,Scala 调用无参数的 numer 和 denom 方法,这种方法用于创建快捷的语法以定义 accessor。Rational 类仍然有 3 个私有字段:n、d 和 g,但是,其中的 n 和 d 被默认定义为私有访问,而 g 则被显式地定义为私有访问,它们对于外部都是隐藏的。
此时,Java 程序员可能会问:“n 和 d 各自的 ‘setter’ 在哪里?” Scala 中不存在这样的 setter。Scala 的一个强大之处就在于,它鼓励开发人员以默认方式创建不可改变的对象。但是,也可使用语法创建修改 Rational 内部结构的方法,但是这样做会破坏该类固有的线程安全性。因此,至少对于这个例子而言,我将保持 Rational 不变。
当然还有一个问题,如何操纵 Rational 呢?与 java.lang.String 一样,不能直接修改现有的 Rational 的值,所以惟一的办法是根据现有类的值创建一个新的 Rational,或者从头创建。这涉及到 4 个名称比较古怪的方法:+、 -、* 和 /。
与其外表相反,这并非操作符重载。
--------------------------------------------------------------------------------
回页首
操作符
记住,在 Scala 中一切都是对象。在上一篇 文章 中, 您看到了函数本身也是对象这一原则的应用,这使 Scala 程序员可以将函数赋予变量,将函数作为对象参数传递等等。另一个同样重要的原则是,一切都是函数;也就是说,在此处,命名为 add 的函数与命名为 + 的函数没有区别。在 Scala 中,所有操作符都是类的函数。只不过它们的名称比较古怪罢了。
在 Rational 类中,为有理数定义了 4 种操作。它们是规范的数学操作:加、减、乘、除。每种操作以它的数学符号命名:+、-、 * 和 /。
但是请注意,这些操作符每次操作时都构造一个新的 Rational 对象。同样,这与 java.lang.String 非常相似,这是默认的实现,因为这样可以产生线程安全的代码(如果线程没有修改共享状态 —— 默认情况下,跨线程共享的对象的内部状态也属于共享状态 —— 则不会影响对那个状态的并发访问)。
有什么变化?
一切都是函数,这一规则产生两个重要影响:
首先,您已经看到,函数可以作为对象进行操纵和存储。这使函数具有强大的可重用性,本系列 第一篇文章 对此作了探讨。
第二个影响是,Scala 语言设计者提供的操作符与 Scala 程序员认为应该 提供的操作符之间没有特别的差异。例如,假设提供一个 “求倒数” 操作符,这个操作符会将分子和分母调换,返回一个新的 Rational (即对于 Rational(2,5) 将返回 Rational(5,2))。如果您认为 ~ 符号最适合表示这个概念,那么可以使用此符号作为名称定义一个新方法,该方法将和 Java 代码中任何其他操作符一样,如清单 5 所示:
在 Scala 中定义这种一元 “操作符” 需要一点技巧,但这只是语法上的问题而已:
当然,需要注意的地方是,必须在名称 ~ 之前加上前缀 “unary_”,告诉 Scala 编译器它属于一元操作符。因此,该语法将颠覆大多数对象语言中常见的传统 reference-then-method 语法。
这条规则与 “一切都是对象” 规则结合起来,可以实现功能强大(但很简单)的代码:
Scala 编译器甚至会尝试推断具有某种预定含义的 “操作符” 的其他含义,例如 += 操作符。注意,虽然 Rational 类并没有显式地定义 +=,下面的代码仍然会正常运行:
打印结果时,r5 的值为 [13 / 12],结果是正确的。
--------------------------------------------------------------------------------
回页首
Scala 内幕
记住,Scala 将被编译为 Java 字节码,这意味着它在 JVM 上运行。如果您需要证据,那么只需注意编译器生成以 0xCAFEBABE 开头的 .class 文件,就像 javac 一样。另外请注意,如果启动 JDK 自带的 Java 字节码反编译器(javap),并将它指向生成的 Rational 类,将会出现什么情况,如清单 9 所示:
Scala 类中定义的 “操作符” 被转换成传统 Java 编程中的方法调用,不过它们仍使用看上去有些古怪的名称。类中定义了两个构造函数:一个构造函数带有一个 int 参数,另一个带有两个 int 参数。您可能会注意到,大写的 Int 类型与 java.lang.Integer 有点相似,Scala 编译器非常聪明,会在类定义中将它们转换成常规的 Java 原语 int。
测试 Rational 类
一种著名的观点认为,优秀的程序员编写代码,伟大的程序员编写测试;到目前为止,我还没有对我的 Scala 代码严格地实践这一规则,那么现在看看将这个 Rational 类放入一个传统的 JUnit 测试套件中会怎样,如清单 10 所示:
除了确认 Rational 类运行正常之外,上面的测试套件还证明可以从 Java 代码中调用 Scala 代码(尽管在操作符方面有点不匹配)。当然,令人高兴的是,您可以将 Java 类迁移至 Scala 类,同时不必更改支持这些类的测试,然后慢慢尝试 Scala。
您惟一可能觉得古怪的地方是操作符调用,在本例中就是 Rational 类中的 + 方法。回顾一下 javap 的输出,Scala 显然已经将 + 函数转换为 JVM 方法 $plus,但是 Java 语言规范并不允许标识符中出现 $ 字符(这正是它被用于嵌套和匿名嵌套类名称中的原因)。
为了调用那些方法,需要用 Groovy 或 JRuby(或者其他对 $ 字符没有限制的语言)编写测试,或者编写 Reflection 代码来调用它。我采用后一种方法,从 Scala 的角度看这不是那么有趣,但是如果您有兴趣的话,可以看看本文的代码中包含的结果(参见 下载)。
注意,只有当函数名称不是合法的 Java 标识符时才需要用这类方法。
--------------------------------------------------------------------------------
回页首
“更好的” Java
我学习 C++ 的时候,Bjarne Stroustrup 建议,学习 C++ 的一种方法是将它看作 “更好的 C 语言”(参见 参考资料)。在某些方面,如今的 Java 开发人员也可以将 Scala 看作是 “更好的 Java”,因为它提供了一种编写传统 Java POJO 的更简洁的方式。考虑清单 11 中显示的传统 Person POJO:
现在考虑用 Scala 编写的对等物:
这不是一个完全匹配的替换,因为原始的 Person 包含一些可变的 setter。但是,由于原始的 Person 没有与这些可变 setter 相关的同步代码,所以 Scala 版本使用起来更安全。而且,如果目标是减少 Person 中的代码行数,那么可以删除整个 getFoo 属性方法,因为 Scala 将为每个构造函数参数生成 accessor 方法 —— firstName() 返回一个 String,lastName() 返回一个 String,age() 返回一个 int。
即使必须包含这些可变的 setter 方法,Scala 版本仍然更加简单,如清单 13 所示:
注意,构造函数参数引入了 var 关键字。简单来说, var 告诉编译器这个值是可变的。因此,Scala 同时生成 accessor( String firstName(void))和 mutator(void firstName_$eq(String))方法。然后,就可以方便地创建 setFoo 属性 mutator 方法,它在幕后使用生成的 mutator 方法。
--------------------------------------------------------------------------------
Scala 将函数概念与简洁性相融合,同时又未失去对象的丰富特性。从本系列中您可能已经看到,Scala 还修正了 Java 语言中的一些语法问题(后见之明)。
本文是面向 Java 开发人员的 Scala 指南 系列中的第二篇文章,本文主要讨论了 Scala 的对象特性,使您可以开始使用 Scala,而不必深入探究函数方面。应用目前学到的知识,您现在可以使用 Scala 减轻编程负担。而且,可以使用 Scala 生成其他编程环境(例如 Spring 或 Hibernate )所需的 POJO。
但是,请继续关注本系列,下次将开始讨论 Scala 的函数方面。
Scala 的函数编程特性非常引人注目,但这并非 Java 开发人员应该对这门语言感兴趣的惟一原因。实际上,Scala 融合了函数概念和面向对象概念。为了让 Java 和 Scala 程序员感到得心应手,可以了解一下 Scala 的对象特性,看看它们是如何在语言方面与 Java 对应的。记住,其中的一些特性并不是直接对应,或者说,在某些情况下,“对应” 更像是一种类比,而不是直接的对应。不过,遇到重要区别时,我会指出来。
Scala 和 Java 一样使用类
我们不对 Scala 支持的类特性作冗长而抽象的讨论,而是着眼于一个类的定义,这个类可用于为 Scala 平台引入对有理数的支持(主要借鉴自 “Scala By Example”,参见 参考资料):
清单 1. rational.scala class Rational(n:Int, d:Int) { private def gcd(x:Int, y:Int): Int = { if (x==0) y else if (x<0) gcd(-x, y) else if (y<0) -gcd(x, -y) else gcd(y%x, x) } private val g = gcd(n,d) val numer:Int = n/g val denom:Int = d/g def +(that:Rational) = new Rational(numer*that.denom + that.numer*denom, denom * that.denom) def -(that:Rational) = new Rational(numer * that.denom - that.numer * denom, denom * that.denom) def *(that:Rational) = new Rational(numer * that.numer, denom * that.denom) def /(that:Rational) = new Rational(numer * that.denom, denom * that.numer) override def toString() = "Rational: [" + numer + " / " + denom + "]" }
从词汇上看,清单 1 的整体结构与 Java 代码类似,但是,这里显然还有一些新的元素。在详细讨论这个定义之前,先看一段使用这个新 Rational 类的代码:
清单 2. RunRational class Rational(n:Int, d:Int) { // ... as before } object RunRational extends Application { val r1 = new Rational(1, 3) val r2 = new Rational(2, 5) val r3 = r1 - r2 val r4 = r1 + r2 Console.println("r1 = " + r1) Console.println("r2 = " + r2) Console.println("r3 = r1 - r2 = " + r3) Console.println("r4 = r1 + r2 = " + r4) }
清单 2 中的内容平淡无奇:先创建两个有理数,然后再创建两个 Rational,作为前面两个有理数的和与差,最后将这几个数回传到控制台上(注意, Console.println() 来自 Scala 核心库,位于 scala.* 中,它被隐式地导入每个 Scala 程序中,就像 Java 编程中的 java.lang 一样)。
--------------------------------------------------------------------------------
回页首
用多少种方法构造类?
现在,回顾一下 Rational 类定义中的第一行:
清单 3. Scala 的默认构造函数 class Rational(n:Int, d:Int) { // ...
您也许会认为清单 3 中使用了某种类似于泛型的语法,这其实是 Rational 类的默认的、首选的构造函数:n 和 d 是构造函数的参数。
Scala 优先使用单个构造函数,这具有一定的意义 —— 大多数类只有一个构造函数,或者通过一个构造函数将一组构造函数 “链接” 起来。如果需要,可以在一个 Rational 上定义更多的构造函数,例如:
清单 4. 构造函数链 class Rational(n:Int, d:Int) { def this(d:Int) = { this(0, d) }
注意,Scala 的构造函数链通过调用首选构造函数(Int,Int 版本)实现 Java 构造函数链的功能。
实现细节
在处理有理数时,采取一点数值技巧将会有所帮助:也就是说,找到公分母,使某些操作变得更容易。如果要将 1/2 与 2/4 相加,那么 Rational 类应该足够聪明,能够认识到 2/4 和 1/2 是相等的,并在将这两个数相加之前进行相应的转换。
嵌套的私有 gcd() 函数和 Rational 类中的 g 值可以实现这样的功能。在 Scala 中调用构造函数时,将对整个类进行计算,这意味着将 g 初始化为 n 和 d 的最大公分母,然后用它依次设置 n 和 d。
回顾一下 清单 1 就会发现,我创建了一个覆盖的 toString 方法来返回 Rational 的值,在 RunRational 驱动程序代码中使用 toString 时,这样做非常有用。
然而,请注意 toString 的语法:定义前面的 override 关键字是必需的,这样 Scala 才能确认基类中存在相应的定义。这有助于预防因意外的输入错误导致难于觉察的 bug(Java 5 中创建 @Override 注释的动机也在于此)。还应注意,这里没有指定返回类型 —— 从方法体的定义很容易看出 —— 返回值没有用 return 关键字显式地标注,而在 Java 中则必须这样做。相反,函数中的最后一个值将被隐式地当作返回值(但是,如果您更喜欢 Java 语法,也可以使用 return 关键字)。
--------------------------------------------------------------------------------
回页首
一些重要值
接下来分别是 numer 和 denom 的定义。这里涉及的语法可能让 Java 程序员认为 numer 和 denom 是公共的 Int 字段,它们分别被初始化为 n-over-g 和 d-over-g;但这种想法是不对的。
在形式上,Scala 调用无参数的 numer 和 denom 方法,这种方法用于创建快捷的语法以定义 accessor。Rational 类仍然有 3 个私有字段:n、d 和 g,但是,其中的 n 和 d 被默认定义为私有访问,而 g 则被显式地定义为私有访问,它们对于外部都是隐藏的。
此时,Java 程序员可能会问:“n 和 d 各自的 ‘setter’ 在哪里?” Scala 中不存在这样的 setter。Scala 的一个强大之处就在于,它鼓励开发人员以默认方式创建不可改变的对象。但是,也可使用语法创建修改 Rational 内部结构的方法,但是这样做会破坏该类固有的线程安全性。因此,至少对于这个例子而言,我将保持 Rational 不变。
当然还有一个问题,如何操纵 Rational 呢?与 java.lang.String 一样,不能直接修改现有的 Rational 的值,所以惟一的办法是根据现有类的值创建一个新的 Rational,或者从头创建。这涉及到 4 个名称比较古怪的方法:+、 -、* 和 /。
与其外表相反,这并非操作符重载。
--------------------------------------------------------------------------------
回页首
操作符
记住,在 Scala 中一切都是对象。在上一篇 文章 中, 您看到了函数本身也是对象这一原则的应用,这使 Scala 程序员可以将函数赋予变量,将函数作为对象参数传递等等。另一个同样重要的原则是,一切都是函数;也就是说,在此处,命名为 add 的函数与命名为 + 的函数没有区别。在 Scala 中,所有操作符都是类的函数。只不过它们的名称比较古怪罢了。
在 Rational 类中,为有理数定义了 4 种操作。它们是规范的数学操作:加、减、乘、除。每种操作以它的数学符号命名:+、-、 * 和 /。
但是请注意,这些操作符每次操作时都构造一个新的 Rational 对象。同样,这与 java.lang.String 非常相似,这是默认的实现,因为这样可以产生线程安全的代码(如果线程没有修改共享状态 —— 默认情况下,跨线程共享的对象的内部状态也属于共享状态 —— 则不会影响对那个状态的并发访问)。
有什么变化?
一切都是函数,这一规则产生两个重要影响:
首先,您已经看到,函数可以作为对象进行操纵和存储。这使函数具有强大的可重用性,本系列 第一篇文章 对此作了探讨。
第二个影响是,Scala 语言设计者提供的操作符与 Scala 程序员认为应该 提供的操作符之间没有特别的差异。例如,假设提供一个 “求倒数” 操作符,这个操作符会将分子和分母调换,返回一个新的 Rational (即对于 Rational(2,5) 将返回 Rational(5,2))。如果您认为 ~ 符号最适合表示这个概念,那么可以使用此符号作为名称定义一个新方法,该方法将和 Java 代码中任何其他操作符一样,如清单 5 所示:
清单 5. 求倒数 val r6 = ~r1 Console.println(r6) // should print [3 / 1], since r1 = [1 / 3]
在 Scala 中定义这种一元 “操作符” 需要一点技巧,但这只是语法上的问题而已:
清单 6. 如何求倒数 class Rational(n:Int, d:Int) { // ... as before ... def unary_~ : Rational = new Rational(denom, numer) }
当然,需要注意的地方是,必须在名称 ~ 之前加上前缀 “unary_”,告诉 Scala 编译器它属于一元操作符。因此,该语法将颠覆大多数对象语言中常见的传统 reference-then-method 语法。
这条规则与 “一切都是对象” 规则结合起来,可以实现功能强大(但很简单)的代码:
清单 7. 求和 1 + 2 + 3 // same as 1.+(2.+(3)) r1 + r2 + r3 // same as r1.+(r2.+(r3))当然,对于简单的整数加法,Scala 编译器也会 “得到正确的结果”,它们在语法上是完全一样的。这意味着您可以开发与 Scala 语言 “内置” 的类型完全相同的类型。
Scala 编译器甚至会尝试推断具有某种预定含义的 “操作符” 的其他含义,例如 += 操作符。注意,虽然 Rational 类并没有显式地定义 +=,下面的代码仍然会正常运行:
清单 8. Scala 推断 var r5 = new Rational(3,4) r5 += r1 Console.println(r5)
打印结果时,r5 的值为 [13 / 12],结果是正确的。
--------------------------------------------------------------------------------
回页首
Scala 内幕
记住,Scala 将被编译为 Java 字节码,这意味着它在 JVM 上运行。如果您需要证据,那么只需注意编译器生成以 0xCAFEBABE 开头的 .class 文件,就像 javac 一样。另外请注意,如果启动 JDK 自带的 Java 字节码反编译器(javap),并将它指向生成的 Rational 类,将会出现什么情况,如清单 9 所示:
清单 9. 从 rational.scala 编译的类 C:\Projects\scala-classes\code>javap -private -classpath classes Rational Compiled from "rational.scala" public class Rational extends java.lang.Object implements scala.ScalaObject{ private int denom; private int numer; private int g; public Rational(int, int); public Rational unary_$tilde(); public java.lang.String toString(); public Rational $div(Rational); public Rational $times(Rational); public Rational $minus(Rational); public Rational $plus(Rational); public int denom(); public int numer(); private int g(); private int gcd(int, int); public Rational(int); public int $tag(); } C:\Projects\scala-classes\code>
Scala 类中定义的 “操作符” 被转换成传统 Java 编程中的方法调用,不过它们仍使用看上去有些古怪的名称。类中定义了两个构造函数:一个构造函数带有一个 int 参数,另一个带有两个 int 参数。您可能会注意到,大写的 Int 类型与 java.lang.Integer 有点相似,Scala 编译器非常聪明,会在类定义中将它们转换成常规的 Java 原语 int。
测试 Rational 类
一种著名的观点认为,优秀的程序员编写代码,伟大的程序员编写测试;到目前为止,我还没有对我的 Scala 代码严格地实践这一规则,那么现在看看将这个 Rational 类放入一个传统的 JUnit 测试套件中会怎样,如清单 10 所示:
清单 10. RationalTest.java import org.junit.*; import static org.junit.Assert.*; public class RationalTest { @Test public void test2ArgRationalConstructor() { Rational r = new Rational(2, 5); assertTrue(r.numer() == 2); assertTrue(r.denom() == 5); } @Test public void test1ArgRationalConstructor() { Rational r = new Rational(5); assertTrue(r.numer() == 0); assertTrue(r.denom() == 1); // 1 because of gcd() invocation during construction; // 0-over-5 is the same as 0-over-1 } @Test public void testAddRationals() { Rational r1 = new Rational(2, 5); Rational r2 = new Rational(1, 3); Rational r3 = (Rational) reflectInvoke(r1, "$plus", r2); //r1.$plus(r2); assertTrue(r3.numer() == 11); assertTrue(r3.denom() == 15); } // ... some details omitted }
除了确认 Rational 类运行正常之外,上面的测试套件还证明可以从 Java 代码中调用 Scala 代码(尽管在操作符方面有点不匹配)。当然,令人高兴的是,您可以将 Java 类迁移至 Scala 类,同时不必更改支持这些类的测试,然后慢慢尝试 Scala。
您惟一可能觉得古怪的地方是操作符调用,在本例中就是 Rational 类中的 + 方法。回顾一下 javap 的输出,Scala 显然已经将 + 函数转换为 JVM 方法 $plus,但是 Java 语言规范并不允许标识符中出现 $ 字符(这正是它被用于嵌套和匿名嵌套类名称中的原因)。
为了调用那些方法,需要用 Groovy 或 JRuby(或者其他对 $ 字符没有限制的语言)编写测试,或者编写 Reflection 代码来调用它。我采用后一种方法,从 Scala 的角度看这不是那么有趣,但是如果您有兴趣的话,可以看看本文的代码中包含的结果(参见 下载)。
注意,只有当函数名称不是合法的 Java 标识符时才需要用这类方法。
--------------------------------------------------------------------------------
回页首
“更好的” Java
我学习 C++ 的时候,Bjarne Stroustrup 建议,学习 C++ 的一种方法是将它看作 “更好的 C 语言”(参见 参考资料)。在某些方面,如今的 Java 开发人员也可以将 Scala 看作是 “更好的 Java”,因为它提供了一种编写传统 Java POJO 的更简洁的方式。考虑清单 11 中显示的传统 Person POJO:
清单 11. JavaPerson.java(原始 POJO) public class JavaPerson { public JavaPerson(String firstName, String lastName, int age) { this.firstName = firstName; this.lastName = lastName; this.age = age; } public String getFirstName() { return this.firstName; } public void setFirstName(String value) { this.firstName = value; } public String getLastName() { return this.lastName; } public void setLastName(String value) { this.lastName = value; } public int getAge() { return this.age; } public void setAge(int value) { this.age = value; } public String toString() { return "[Person: firstName" + firstName + " lastName:" + lastName + " age:" + age + " ]"; } private String firstName; private String lastName; private int age; }
现在考虑用 Scala 编写的对等物:
清单 12. person.scala(线程安全的 POJO) class Person(firstName:String, lastName:String, age:Int) { def getFirstName = firstName def getLastName = lastName def getAge = age override def toString = "[Person firstName:" + firstName + " lastName:" + lastName + " age:" + age + " ]" }
这不是一个完全匹配的替换,因为原始的 Person 包含一些可变的 setter。但是,由于原始的 Person 没有与这些可变 setter 相关的同步代码,所以 Scala 版本使用起来更安全。而且,如果目标是减少 Person 中的代码行数,那么可以删除整个 getFoo 属性方法,因为 Scala 将为每个构造函数参数生成 accessor 方法 —— firstName() 返回一个 String,lastName() 返回一个 String,age() 返回一个 int。
即使必须包含这些可变的 setter 方法,Scala 版本仍然更加简单,如清单 13 所示:
清单 13. person.scala(完整的 POJO) class Person(var firstName:String, var lastName:String, var age:Int) { def getFirstName = firstName def getLastName = lastName def getAge = age def setFirstName(value:String):Unit = firstName = value def setLastName(value:String) = lastName = value def setAge(value:Int) = age = value override def toString = "[Person firstName:" + firstName + " lastName:" + lastName + " age:" + age + " ]" }
注意,构造函数参数引入了 var 关键字。简单来说, var 告诉编译器这个值是可变的。因此,Scala 同时生成 accessor( String firstName(void))和 mutator(void firstName_$eq(String))方法。然后,就可以方便地创建 setFoo 属性 mutator 方法,它在幕后使用生成的 mutator 方法。
--------------------------------------------------------------------------------
Scala 将函数概念与简洁性相融合,同时又未失去对象的丰富特性。从本系列中您可能已经看到,Scala 还修正了 Java 语言中的一些语法问题(后见之明)。
本文是面向 Java 开发人员的 Scala 指南 系列中的第二篇文章,本文主要讨论了 Scala 的对象特性,使您可以开始使用 Scala,而不必深入探究函数方面。应用目前学到的知识,您现在可以使用 Scala 减轻编程负担。而且,可以使用 Scala 生成其他编程环境(例如 Spring 或 Hibernate )所需的 POJO。
但是,请继续关注本系列,下次将开始讨论 Scala 的函数方面。
发表评论
-
Scala + Twitter = Scitter(scala代码学习第15天)
2011-04-08 09:11 879Twitter 迅速占领了 Interne ... -
面向 Java 开发人员的 Scala 指南: Scala 和 servlet(scala代码学习第十一天)
2011-04-02 07:40 740Scala 显然是一门有趣的语言,很适合体现语言理论和创新方面 ... -
构建计算器,第 3 部分将 Scala 解析器组合子和 case 类结合起来(scala代码学习第十天)
2011-04-01 09:25 956欢迎勇于探索的读者回到我们的系列文章中!本月继续探索 Scal ... -
scala代码学习构建计算器,第2 部分(代码学习第九天)
2011-03-31 10:53 812回忆一下我们的英雄所处的困境:在试图创建一个 DSL(这里只不 ... -
Scala构建计算器,第1 部分(代码学习第8天)
2011-03-30 11:59 1199特定于领域的语言 可能您无法(或没有时间)承受来自于您的项目 ... -
scala包和访问修饰符(代码学习第七天)
2011-03-29 15:51 1620系列的过程中我遗漏了 ... -
实现继承(代码学习第五天)
2011-03-26 10:13 969近十几年来,面向对象语言设计的要素一直是继承的核心。不支持继承 ... -
关于特征和行为(代码学习第四天)
2011-03-25 09:38 705著名科学家、研究学者 ... -
Scala 控制结构内部揭密(scala代码学习第三天)
2011-03-24 09:15 1314迄今为止,在此 系列 ... -
programming in scala 2nd代码学习(第一天)
2011-03-22 18:42 941近来没事,拿出了原先学习scala的代码 书中代码噢、拿出自己 ... -
scalatra web框架快速搭建(官方使用文档)
2011-03-21 22:42 2523昨天写了个sbt构建scala项目的文章,就是为了今天的sca ... -
A build tool for Scala(simple-build-tool) sbt安装指南
2011-03-20 22:49 2207今天有位写框架的大哥叫我学一学scalatra框架,找了 ... -
Scala functional style deferent from java OOP(特点)
2011-03-20 17:34 988该程序通过一段斐波那契数列的计算,比较一下Scala的函数式编 ... -
Java 开发人员的 Scala 指南: 面向对象的函数编程
2011-03-20 11:59 1046函数概念 开始之前, ...
相关推荐
### 开发人员的Scala指南 #### 一、引言 《开发人员的Scala指南》是一部专为希望深入了解Scala编程语言的Java开发者所编写的指南。本书由Ted Neward撰写,共15章,总计188页。Ted Neward是一位经验丰富的软件架构...
Scala是一种融合了函数式和面向对象编程特点的静态类型语言,特别适合在Java虚拟机(JVM)上开发能够充分利用多核处理器性能的应用。 本书第一章介绍了Scala的基本概念,包括函数式编程的理念。函数式编程是一种...
本书的版权信息显示,最初的PrePrint™版首次发布是在2007年,而第二版的发行是在2008年2月18日。书籍的版权声明属于Martin Odersky、Lex Spoon和Bill Venners三人,这反映了他们共同为Scala语言及其文档所做出的...
《快学 Scala(第二版)》是专为希望快速掌握Scala编程的读者编写的指南,尤其适合已经有一定编程基础的开发者。尽管描述为英文版,但作者通过简洁明了的表述,使得学习过程更为直观易懂。 **Scala基础知识** 1. **...
通过阅读《Scala程序设计第二版》,开发者不仅可以掌握Scala语言的基础知识,还能了解到如何利用Scala进行高效的并发编程、构建大规模分布式系统,以及如何在实际开发中发挥Scala的灵活性和表达力。对于希望进入...
**第2章:Scala的第一步** - **学习路径**: - 使用Scala解释器。 - 定义变量。 - 定义函数。 - 编写Scala脚本。 - 使用`while`循环和`if`判断。 - 使用`foreach`和`for`枚举。 **第3章:Scala的下一步** - *...
理解Scala的基础语法是学习这门语言的第一步。 1. Scala与Java的关系 Scala是由Martin Odersky设计的,它是在Java的基础上发展起来的,共享了JVM的生态系统。这意味着Scala程序可以无缝地调用Java库,并且Java...
Scala是一门主要以Java虚拟机(JVM)为目标运行环境并将面向对象和函数式编程语言的*佳特性结合在一起的编程语言。你可以使用Scala编写出更加精简的程序,同时充分利用并发的威力。由于Scala默认运行于JVM之上,因此...
通过上述步骤,我们不仅成功搭建了Scala的开发环境,还集成了IntelliJ IDEA和Eclipse两个主流IDE的Scala插件,为后续的Scala学习与项目开发打下了坚实的基础。Scala凭借其独特的魅力和强大的功能,正逐渐成为开发者...
《Programming in Scala》第二版是一本全面详尽的Scala编程指南,由Scala语言的主要设计者Martin Odersky、Lex Spoon和Bill Venners共同撰写。本书是为Scala 2.8版本更新的,因此包含了最新的特性和技术改进。 ####...
《Programming in Scala》第二版...总之,《Programming in Scala》第二版是学习Scala语言的权威指南,通过深入学习此书,开发者可以全面理解Scala的语法、设计理念和最佳实践,从而在实际项目中充分利用Scala的优势。
- **第二部分**:使用Scala进行实际工作(6-8章)。 - **章节概览**: - **第1章:为什么选择Scala?** - **内容要点**:介绍Scala语言的优势及其与其他语言的对比。 - **目标读者**:对Scala感兴趣但尚未入门的...
9. **对象和类**:Scala是面向对象的语言,但它也支持单例对象,这在某些情况下可以替代传统的Java静态方法,提供了更清晰的面向对象模型。 10. **Dotty与Scala 3**:虽然本书基于Scala的第三版,但也可以探讨Dotty...
**编程语言Scala深度解析**...通过《Programming in Scala》第三版,读者可以系统地学习这些特性,掌握Scala编程技巧,从而在现代软件开发中充分利用其强大的功能。书中的实例和练习将帮助读者深化理解,提升编程能力。
**Scala实用指南1** 是一本面向Java开发者,旨在教授Scala编程语言的实用指南。作者文卡特·苏帕拉马尼亚姆(Venkat Subramaniam)是一位知名的编程专家,他在Agile Developer公司担任创始人,并在休斯敦大学任教,...
### Scala 快速指南知识点详解 #### 一、Scala简介 **Scala**(可伸缩语言)是一种结合了面向对象编程与函数式编程特性的混合编程语言。它由Martin Odersky于2003年首次发布。由于Scala能够在保持语言简洁性的同时...
7. **互操作性**:由于Scala是基于JVM的,因此可以无缝地与Java代码交互,利用已有的Java库,同时也被广泛用于构建Java平台上的高性能应用。 8. **DSL(领域特定语言)**:Scala的语法灵活性使其非常适合构建内部...
"scala-intellij-bin-2023.1.15.zip" 是一个包含IntelliJ IDEA针对Scala开发的特定版本的二进制发行包。 这个压缩包很可能包含了以下内容: 1. **IntelliJ IDEA安装程序**:这是主应用程序,允许用户在本地计算机...