没有合适的资源?快使用搜索试试~ 我知道了~
资源推荐
资源详情
资源评论
1. 给 JAVA 程序设计师的 SCALA 入门教学
此教学将对 Scala 语言以及编译器做一个简易介绍。设定的读者为具有程设经验且想要看
Scala 功能概要的人。内文假设读者有着基本、特别是 Java 上的对象导向程设知识。
1.1. 第一个例子
这边用标准的
Hello world
程序作为第一个例子。虽然它很无趣,可是这让我们在仅用少量语
言特性下演示 Scala 工具。程序如下:
object HelloWorld {
def main(args: Array[String]) {
println("Hello, world!")
}
}
Java 程序员应该对这个程序结构感到熟悉:有着一个 main 函数,该函数接受一个字符串阵列
自变量,也就是命令列自变量;函数内容为呼叫已定义好的函数 println 并用 Hello world 字符串
当自变量。 main 函数没有回传值 (它是程序函数)。因此并不需要宣告回传型别。
Java 程序员 不太熟悉的是包着 main 函数的 object 宣告。 这种宣告引入我们一般称之
Singleton
的东西,也就是只有一个实体的类别。所以上面的宣告同时宣告了一个 HelloWorld 类别
跟一个这类别的实体,也叫做 HelloWorld。该实体会在第一次被使用到的时候实时产生。
眼尖的读者可能已经注意到这边 main 函数的宣告没有带着 static。这是因为 Scala 没有静态
成员 (函数或资料栏)。Scala 程序员将这成员宣告在单实例对象中,而不是定义静态成员。
1.1.1. 编译这例子
我们用 Scala 编译器 scalac 来编译这个例子。scalac 就像大多数编译器一样,它接受原码档
当自变量,并接受额外的选项,然后产生一个或多个对象档。它产出的对象档为标准 Java class 档
案。
如果我们将上面的程序存成 HelloWorld.scala 档,编译指令为( > 是提示字符,不用打):
> scalac HelloWorld.scala
这会在当前目录产生一些 class 档案。其中一个会叫做 HelloWorld.class,里面包含着可被
scala 直接执行的类别。
1.1.2. 执行范例
一旦编译过后,Scala 程序可以用 scala 指令执行。其使用方式非常像执行 Java 程序的 java
指令,并且接受同样选项。上面的范例可以用以下指令来执行并得到我们预期的输出:
> scala -classpath . HelloWorld
Hello, world!
1.2.
与
Java
互动
Scala 的优点之一是它非常容易跟 Java 程序码沟通。预设汇入所有 java.lang 底下之类别,其
他类别则需要明确汇入。
让我们看个展示这点的范例。取得当下日期并根据某个特定国家调整成该国格式,如法国。
Java 的标准函数库定义了一些有用的工具类别,如 Date 跟 DateFormat。因为 Scala 可以无
缝的跟 Jav a 互动,这边不需要以 Scala 实作同样类别-我们只需要汇入对应的 Java 套件:
import java.util.{Date, Locale}
import java.text.DateFormat
import java.text.DateFormat._
object FrenchDate {
def main(args: Array[String]) {
val now = new Date
val df = getDateInstance(LONG, Locale.FRANCE)
println(df format now)
}
}
Scala 的汇入陈述式跟 Java 非常像,但更为强大。如第一行,同一个 package 下的多个类别
可以用大括号括起来一起导入。另外一个差别是,当要汇入套件或类别下所有名称时,用下标 (_) 而
不是星号 (*)。这是因为星号在 Scala 是一个合法的识别符号 (如函数名称)。
所以第三行的陈述式导入所有 DateFormat 类别的成员。这让静态函数 getDateInstance 跟静态
资料栏 LONG 可直接被使用。
在 main 函数中我们先创造一个 Java 的 Date 类别实体,该实体预设拥有现在的日期。接下来
用 getDateInstance 函数定义日期格式。最后根据地区化的 DateFormat 实体对现在日期设定格式
并印出。最后一行展现了一个 Scala 有趣特点。只需要一个自变量的函数可以用中缀语法呼叫。就
是说,这个表示式
df format now
是比较不详细版本的这个表示式
df.format(now)
这点也许看起来只是语法上的小细节,但是它有着重要的后果,其中一个将会在下一节做介绍。
最后值得一提的是,Scala 可以直接继承 Java 类别跟实作 Java 界面。
1.3. 万物皆对象
Scala 是一个纯粹的对象导向语言,这句话的意思是说,
所有东西
都是对象,包括数字、函数。
因为 Java 将基本型别 (如 boolean 与 int ) 跟参照型别分开,而且没有办法像操作变量一样操作
函数,从这角度来看 Scala 跟 Java 是不同的。
1.3.1. 数字是对象
因为数字是对象,它们也有函数。事实上,一个像底下的算数表示式:
1 + 2 * 3 / x
只有使用函数呼叫,因为像前一节一样,该式等价于
(1).+(((2).*(3))./(x))
这也表示着 +、* 之类的在 Scala 里是合法的识别符号。
因为 Scala 的词法分析器对于符号采用最长匹配,在第二版的表示式当中,那些括号是必要的。
也就是说分析器会把这个表示式:
1.+(2)
拆成 1.、+、2 这三个符号。会这样拆分是因为 1. 既是合法匹配同时又比 1 长。 1. 会被解
释成字面常数 1.0,使得它被视为 Double 而不是 Int。把表示式写成:
(1).+(2)
可以避免 1 被解释成 Double。
1.3.2. 函数是对象
可能令 Java 程序员更为惊讶的会是,Scala 中函数也是对象。因此,将函数当做自变量传递、
把它们存入变量、从其他函数返回函数都是可能的。能够像操作变量一样的操作函数这点是
函数编程
这一非常有趣的程设典范的基石之一。
为何把函数当做变量一样的操作会很有用呢,让我们考虑一个定时函数,功能是每秒执行一些动
作。我们要怎么将这动作传给它?最直接的便是将这动作视为函数传入。应该有不少程序员对这种简
单传递函数的行为很熟悉:通常在使用者界面相关的程序上,用以注册一些当事件发生时被呼叫的回
呼函数。
在接下来的程序中,定时函数叫做 oncePerSecond ,它接受一个回呼函数做参数。该函数的型
别被写作 () => Unit ,这个型别便是所有无自变量且无返回值函数的型别( Unit 这个型别就象是
C/C++ 的 void )。此程序的主函数只是呼叫定时函数并带入回呼函数,回呼函数输出一句话到终端
上。也就是说这个程序会不断的每秒输出一次 “time flies like an arrow”。
object Timer {
def oncePerSecond(callback: () => Unit) {
while (true) { callback(); Thread sleep 1000 }
}
def timeFlies() {
println("time flies like an arrow...")
}
def main(args: Array[String]) {
oncePerSecond(timeFlies)
}
}
值得注意的是,这边输出时我们使用 Scala 的函数 println,而不是 System.out 里的函数。
1.3.2.1. 匿名函数
这程序还有改进空间。第一点,函数 timeFlies 只是为了能够被传递进 oncePerSecond 而定义
的。赋予一个只被使用一次的函数名字似乎是没有必要的,最好能够在传入 oncePerSecond 时构造
出这个函数。Scala 可以藉由
匿名函数
来达到这点。利用匿名函数的改进版本程序如下:
object TimerAnonymous {
def oncePerSecond(callback: () => Unit) {
while (true) { callback(); Thread sleep 1000 }
}
def main(args: Array[String]) {
oncePerSecond(() =>
println("time flies like an arrow..."))
}
}
这例子中的右箭头 => 告诉我们有一个匿名函数,右箭头将函数自变量跟函数内容分开。这个例
子中,在箭头左边那组空的括号告诉我们自变量列是空的。函数内容则是跟先前的 timeFlies 里一
样。
1.4. 类别
之前已讲过,Scala 是一个对象导向语言,因此它有着类别的概念 (更精确的说,的确有一些对
象导向语言没有类别的概念,但是 Scala 不是这类)。Scala 宣告类别的语法跟 Java 很接近。一个
重要的差别是,Scala 的类别可以有参数。这边用底下复数的定义来展示:
class Complex(real: Double, imaginary: Double) {
def re() = real
def im() = imaginary
}
这个复数类别接受两个参数,分别为实跟虚部。在创造 Complex 的实体时,必须传入这些参数:
new Complex(1.5, 2.3)。这个类别有两个函数分别叫做 re 跟 im 让我们取得这两个部分。
值得注意的是,这两个函数的回传值并没有被明确给定。编译器将会自动的推断,它会查看这些
函数的右侧并推导出这两个函数都会回传型别为 Double 的值。
编译器并不一定每次都能够推断出型别,而且很不幸的是我们并没有简单规则以分辨哪种情况能
推断,哪种情况不能。因为当编译器无法推断未明确给定的型别时它会回报错误,实务上这通常不是
问题。Scala 初学者在遇到那些看起来很简单就能推导出型别的情况时,应该尝试着忽略型别宣告并
看看编译器是不是也觉得可以推断。多尝试几次之后程序员应该能够体会到何时忽略型别、何时该明
确指定。
1.4.1. 无自变量函数
函数 re、im 有个小问题,为了呼叫函数,我们必须在函数名称后面加上一对空括号,如这个例
子:
object ComplexNumbers {
def main(args: Array[String]) {
val c = new Complex(1.2, 3.4)
println("imaginary part: " + c.im())
}
}
最好能够在不需要加括号的情况下取得实虚部,这样便象是在取资料栏。Scala 完全可以做到这
件事,需要的只是在定义函数的时候
不要定义自变量
。这种函数跟零自变量函数是不一样的,不论是
定义或是呼叫,它们都没有括号跟在名字后面。我们的 Complex 可以改写成:
class Complex(real: Double, imaginary: Double) {
def re = real
def im = imaginary
}
1.4.2. 继承与覆写
Scala 中所有的类别都继承自一个母类别。像前一节的 Complex 这种没有指定的例子,Scala 会
暗中使用 scala.AnyRef。
Scala 中可以覆写继承自母类别的函数。但是为了避免意外覆写,必须加上 override 修饰字来
明确表示要覆写函数。我们以覆写 Complex 类别中来自 Object 的 toString 作为范例。
class Complex(real: Double, imaginary: Double) {
def re = real
def im = imaginary
override def toString() =
"" + re + (if (im < 0) "" else "+") + im + "i"
}
剩余112页未读,继续阅读
资源评论
蚂蚁拾贝
- 粉丝: 5
- 资源: 2
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
- springboot项目家乡特色推荐系统.zip
- 电源开关电源200W 12V 24V,0.95效率 集成PFC+LLC方案稳定,电路外围简单,工作稳定,多重保护,低纹波,低成本,超高效率,芯片好买 电源架构PFC+LLC+同步整流,高效率高功率
- springboot项目基于vue的地方美食分享网站.zip
- springboot项目基于web的智慧养老平台.zip
- springboot项目基于Web的社区医院管理服务系统.zip
- springboot项目基于Springboot的漫画网站.zip
- springboot项目基于vue的MOBA类游戏攻略分享平台.zip
- springboot项目基于SpringBoot的冬奥会科普平台.zip
- [Matlab Simulink] 电动汽车制动能量回收 刹车充电仿真 PMSM永磁同步电机转速SVPWM控制 双有源桥DAB移相控制 电动汽车充放电 个人搭建,确保运行
- springboot项目基于Java的超市进销存系统.zip
- springboot项目基于Spring Boot的在线考试系统.zip
- springboot项目基于SpringBoot的CSGO赛事管理系统.zip
- springboot项目广场舞团.zip
- springboot项目高校食堂移动预约点餐系统.zip
- springboot项目会员制医疗预约服务管理信息系统.zip
- springboot项目福聚苑社区团购.zip
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功