来自:think in java (3)
外文原文
The busy Java developer#39;s guide to Scala: Class action
It makes sense for Javatrade; developers to use objects as a first point of reference for understanding Scala. In this second installment of The busy Java developer#39;s guide to Scala series, Ted Neward follows a basic premise of language measurement: that the power of a language can be measured in direct relation to its ability to integrate new facilities -- in this case, support for complex numbers. Along the way you#39;ll see some interesting tidbits related to class definitions and usage in Scala. In last month#39;s article , you saw just a touch of Scala#39;s syntax, the bare minimum necessary to run a Scala program and observe some of its simpler features. The Hello World and Timer examples from that article let you see Scala#39;s Application class, its syntax for method definitions and anonymous functions, just a glimpse of an Array[], and a bit on type-inferencing. Scala has a great deal more to offer, so this article investigates the intricacies of Scala coding.
Scala#39;s functional programming features are compelling, but they#39;re not the only reason Java developers should be interested in the language. In fact, Scala blends functional concepts and object orientation. In order to let the Java-cum-Scala programmer feel more at home, it makes sense to look at Scala#39;s object features and see how they map over to Java linguistically. Bear in mind that there isn#39;t a direct mapping for some of these features, or in some cases, the 'mapping' is more of an analog than a direct parallel. But where the distinction is important, I#39;ll point it out.
Scala has class(es), too
Rather than embark on a lengthy and abstract discussion of the class features that Scala supports, let#39;s look at a definition for a class that might be used to bring rational number support to the Scala platform (largely swiped from 'Scala By Example' -- see Resources):
Listing 1. rational.scala
class Rational(n:Int, d:Int) { private def gcd(x:Int, y:Int): Int = { if (x==0) y else if (xlt;0) gcd(-x, y) else if (ylt;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 ']' } |
While the overall structure of Listing 1 is lexically similar to what you#39;ve seen in Java code over the last decade, some new elements clearly are at work here. Before picking this definition apart, take a look at the code to exercise the new Rational class:
Listing 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) } |
What you see in Listing 2 isn#39;t terribly exciting: I create a couple of rational numbers, create two more Rationals as the addition and subtraction of the first two, and echo everything to the console. (Note that Console.println() comes from the Scala core library, living in scala.*, and is implicitly imported into every Scala program, just as java.lang is in Java programming.)
How many ways shall I construct thee?
Now look again at the first line in the Rational class definition:
Listing 3. Scala#39;s default constructor
class Rational(n:Int, d:Int) { // ... |
Although you might think you#39;re looking at some kind of generics-like syntax in Listing 3, it#39;s actually the default and preferred constructor for the Rational class: n and d are simply the parameters to that constructor.
Scala#39;s preference for a single constructor makes a certain kind of sense -- most classes end up having a single constructor or a collection of constructors that all 'chain' through a single constructor as a convenience. If you wanted to, you could define more constructors on a Rational like so:
Listing 4. A chain of constructors
class Rational(n:Int, d:Int) { def this(d:Int) = { this(0, d) } |
Note that Scala#39;s constructor chain does the usual Java-constructor-chaining thing by calling into the preferred constructor (the Int,Int version).
Details, (implementation) details...
When working with rational numbers, it helps to perform a bit of numerical legerdemain: namely that of finding a common denominator to make certain operations easier. If you want to add 1-over-2 (also known as 'one-half') to 2-over-4 (also known as 'two-fourths'), the Rational class should be smart enough to realize that 2-over-4 is the same as 1-over-2, and convert it accordingly before adding the two together.
This is the purpose of the nested private gcd() function and g value inside of the Rational class. When the constructor is invoked in Scala, the entire body of the class is evaluated, which means g will be initialized with the greatest common denominator of n and d, and t
剩余内容已隐藏,支付完成后下载完整资料
外文翻译
面向Java开发人员的Scala指南:类操作
Javatrade; 开发人员可以将对象作为理解Scala的出发点。本文是面向Java开发人员的 Scala指南系列的第二期,作者Ted Neward遵循对一种语言进行评价的基本前提:一种语言的威力可以直接通过它集成新功能的能力衡量,在本文中就是指对复数的支持。跟随本文,您将了解在Scala中与类的定义和使用有关的一些有趣特性。
在上一期文章中,您只是稍微了解了一些 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. rational.scala
class Rational(n:Int, d:Int) { private def gcd(x:Int, y:Int): Int = { if (x==0) y else if (xlt;0) gcd(-x, y) else if (ylt;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各自的lsquo;setterrsquo;在哪里?”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],结果是正确的。
以上是毕业论文外文翻译,课题毕业论文、任务书、文献综述、开题报告、程序设计、图纸设计等资料可联系客服协助查找。