第十四章 断言和单元测试
最后更新于:2022-04-01 20:16:02
**断言**和**单元测试**是检查软件的行为是否符合需要的两种重要方式。本章将展示Scala中编写和执行它们的若干选择。
-
断言
- 是对预定义方法assert的调用。
- 表现形式:
- assert(condition) //在条件不成立时抛出AssertionError。还可以用在某方法执行结束处且在返回结果之前,用来检查作为返回结果的值是否满足条件,如果断言成立,则返回val值。ensuring方法可以简化这些操作,在此不举例说明。
- assert(condition, explanation) //将在条件不成立时抛出指定的explanation(Any类型)
注:断言可以使用JVM的-ea和-da命令行标志开放和禁止。开放时,每个断言被当作对使用软件运行时产生的实际数据进行的小测试。
-
单元测试
- Scala实现单元测试的方式有很多
- 从Java实现的工具:JUnit和TestNG
- Scala编写的新工具:ScalaTest、specs(本人比较喜欢的测试方法)和ScalaCheck
- 最简单的方法
- 创建扩展org.scalatest.Suite的类并在其中定义测试方法
- Suite代表一个测试集
- 测试方法以”test”开头
- 在Scala解释器中可以使用execute方法运行Suite
- execute方法可以在子类中重载,因此ScalaTest可以为不同风格的测试提供便利
- ScalaTest中的FunSuite特质重载了execute方法,从而可以用函数值的方式来进行测试,而不需要再用方法定义的方式。这样就不需要再用test开头命名所有测试。
-
翔实的失败报告
- 在ScalaTest中使用 “===” 这样的符号,在断言失败时,会返回详细的错误信息。它只能说明左侧的操作元不等于右侧的操作元,如“3 did not equal 2”
- 如果需要强调这种区分,可以使用ScalaTest中的expect方法,该方法会得到更加详细的信息。如“Expected 2, but got 3”
- 如果想检查方法是否出现了期待的异常,可以使用ScalaTest的intercept方法。
这些方法的目的都在于帮助我们写出更加简明清晰的基于断言的测试。
-
JUnit代码
~~~
import junit.framework.TestCase
import junit.framework.Assert.assertEquals
import junit.framework.Assert.fail
import Element.elem
class ElementTestCase extends TestCase {
def testUniformElement() {
val ele = elem('x', 2, 3)
assertEquals(2, ele.width)
assertEquals(3, ele.height)
try {
elem('x', 2, 3)
fail()
}
catch {
case e: IllegalArgumentException => // expected
}
}
}
~~~
- TestNG代码
~~~
import org.testng.annotations.Test
import org.testng.Assert.assertEquals
import Element.elem
class ElementTests {
@Test def verifyUniformElement() {
val ele = elem('x', 2, 3)
assertEquals(ele.width, 2)
assertEquals(ele.height, 3)
}
@Test {
val expectedExceptions =
Array(classOf[IllegalArgumentException])
}
def elemShouldThrowIAE() { elem('x', 2, 3) }
}
~~~
- 规格测试
- ScalaTest中包含了Spec,便于行为驱动开发(BDD)这种测试风格的测试。
- Spec包括两个部分
- 描述部分
- 写为describe,然后是带括号的字符串和代码块
- 它描述了要被规格化和测试的“目标”
- 规格部分
- 写为it,然后是带括号的字符串和代码块
- (在字符串中)规格化了目标的一小块行为,并(在代码快中)提供了验证这种行为的代码
- 示例代码
~~~
import org.scalatest.Spec
class ElementSpec extends Spec {
describe("A UniformElement") {
it("should have a width equal to the passed value") {
val ele = elem('x', 2, 3)
assert(ele.width === 2)
}
it("should have a height equal to the passed value") {
val ele = elem('x', 2, 3)
assert(ele.height === 3)
}
it("should throw an IAE if passed a negative width") {
intercept(classOf[IllegalArgumentException]) {
elem('x', 2, 3)
}
}
}
}
~~~
~~~
在解释器中调用execute方法,产生的输出读起来很像规格说明。
~~~
- 基于属性的测试
- ScalaCheck能指定待测代码须遵循的属性。
- 对于每个属性,ScalaCheck将产生测试数据并运行测试以验证其是否正确
~~~
import org.scalatest.prop.FunSuite
import org.scalacheck.Prop._
import Element.elem
class ElementSuite extends FunSuite {
test("elem result should have passed width", (w: Int) =>
w > 0 ==> (elem('x', w, 3).width == w)
)
test("elem result should have passed height", (h: Int) =>
h > 0 ==> (elem('x', 2, h).height == h)
)
}
~~~
~~~
其中 "==>" 是含义操作符。说明当前左侧的表达式为真时,那么右侧的表达式也必须为真。
~~~
';
第十三章 包和引用
最后更新于:2022-04-01 20:16:00
在做一个很大的程序的时候,使耦合(程序各个部分依赖于其他部分的程度)最小化十分重要!
减少耦合性的方式之一是使用模块化风格编写代码。将程序分解成若干比较小的模块,把每块分成内部和外部。
在模块内部工作时,只需要和同样工作于这个模块的程序员交互。
只有必须要改变模块的外部时,才需要和工作于其他模块的开发人员交互。
-
包
- Scala的代码采用了Java平台完整的机制,两种方式将代码放入包中
- 通过把package子句放在文件顶端的方式把整个文件内容放进包里
- 在package子句之后把要放到包里的定义用花括号括起来,该方法可以把文件的不同部分放在不同的包里
- Scala的包是嵌套的,命名包时不需要从根部开始,而java必须要从根开始
-
引用
- 包和其内成员可以用关键字 import 子句来引用
- 例如 import java.io.File; 之后就可以用File来进行操作,而不需要写全称
- Scala的引用可以出现在任何地方,且可以指向任意值
- 可以重命名或隐藏一些被引用的成员
- import Fruits.{Apple,Orange}; //此例只引用了对象Fruits的Apple和Orange成员
- import Fruits.{Apple => McIntosh,Orange}; //引用对象Fruits的Apple成员时可以用McIntosh来代替
- 总结
- 简单名x,把x包含进引用名集
- 重命名子句x => y,让名为x的成员以名称y出现
- 隐藏子句x => _,把x排除在引用名集之外
- 全包括 ‘_’,引用除了前面子句提到的之外的全体成员
-
隐式引用
- import java.lang._
- import scala._
- import Predef._
-
访问修饰符
- 私有成员,处理方式与Java相同
- 保护成员,处理方式比Java严格,只在定义了成员的类的子类中可以被访问,而Java是在同一个包内都可以
- 公有成员,没有被标记的都是公有的,没有显示的修饰符
';
第十二章 特质
最后更新于:2022-04-01 20:15:58
- 特质(trait)是Scala里代码复用的基础单元,特质封装了方法和字段的定义,并可以通过混入到类中重用它们。
- 特质是如何工作的
- 关键字 tarit 来定义特质
- 一旦定义了就可以使用extends或with关键字,将其混入类中
- 特质类型的变量可以被任何混入该特质的类对象初始化
- 使用关键字 with 可以混入多个特质,只混入一个特质时,用关键字 extends
- 特质与类的不同
- 特质不能有任何“类”参数,即传递给类的主构造器的参数
- 特质的super是动态绑定的,定义特质时,super调用的方法实现尚未被定义,每次被混入到具体的类中才会被决定
- Scala在特质中添加一次方法,在混入它的类中就不需要重新实现它
- 特质的堆叠
- 给代码带来了极大的灵活性
- 与多重继承的差别
- super方法的调用是由类和被混入到类的特质的线性化所决定的
- 对于多重继承来说,super调用导致的方法调用可以在调用发生的地方明确决定
- 何时用特质 要实现一个可重用的行为集合时,必须决定是使用特质还是抽象类
- 如果行为不会被重用,那么久把它做成具体类,具体类没有可重用的行为
- 如果要在多个不相关的类中重用,就做成特质,只有特质可以混入到不同的类层级中
- 如果希望从Java代码中继承,就使用抽象类
- 如果计划以编译后的方式发布,并且希望外部组织能够写一些继承自它的类,那么使用抽象类会更好一些
- 如果效率非常重要,则应该倾向于使用类
- 如果还是不知道。。。。那么就使用特质吧!
';
第十一章 Scala的层级
最后更新于:2022-04-01 20:15:56
Any是所有其他类的超类,Nothing是所有其他类的子类
-
Scala的层级
- Any类中的方法
- final def == (that : Any) : Boolean
- final def != (that : Any) : Boolean
- def equals(that: Any) : Boolean
- def hashCode : Int
- def toString : String
- 两个子类
- AnyVal是Scala里每个内建值类的父类,除了Java中的八种基本类型还要加上Scala特有的Unit(实例值是 “”“()”“” ),类似于void
- AnyRef是所有引用类的基类,实际就是类Java.lang.Object的别名,但在Scala程序里推荐使用AnyRef
- 不同
- Scala类还继承自一个名为ScalaObject的特别的记号特质。为了加速模式匹配。
-
原始类型是如何实现的
- Scala中 == 操作被设计为对类型表达透明
- 对值类型来说,就是自然的相等
- 对引用类型来说,它被视为继承自Object的equals方法的别名
- 该方法初始地定义为引用相等,但被许多子类重写以实现他们自然理念上的相等性
-
底层类型
- Null类是null引用对象的类型,它是每个引用类的子类,且不兼容值类型
- Nothing类型在Scala的类层级的最底端,它是任何其他类型的子类型。然而根本没有这个类型的任何值。它的用处是表明不正常的终止
';
第十章 组合与继承
最后更新于:2022-04-01 20:15:53
第六章介绍了Scala面向对象的基本概况,本章会比较类之间的两种基本关系:组合与继承,除此之外,还将讨论更多面向对象的细节。
-
组合
- 指一个类特有另一个的引用,借助被引用的类完任务。
-
继承
- 基础是超类/子类的关系。
-
抽象类
- 具有抽象成员的类本身必须被声明为抽象的,用关键字 abstract
- 只被声明但没有实现的方法称为抽象方法
-
定义无参数方法 无参数方法的风格即省略空括号
- 通常,只要方法中没有参数并且方法仅能通过读取所包含对象的属性去访问可变状态,就使用无参数方法
- 取值时,不写括号
- 改值时,写括号
-
扩展类
- 就是Java中的派生
- 继承表示超类的所有成员也是子类的成员
- 超类的私有成员不会被子类继承
- 超类中的成员若和子类中有相同的名称和参数则不会被子类继承
-
重写方法和字段
- 字段和方法术语相同的命名空间
- 字段可以重写无参数方法
- 支持统一访问原则
- Scala禁止在同一个类里用同样的名称定义字段和方法
- 命名空间
- Java有四个命名空间:字段、方法、类型、包
- Scala只有两个命名空间:值(字段、方法、包还有单例对象),类型(类和特质名)
-
定义参数化字段
~~~
class Cat {
val dangerous = false;
}
class Tiger {
override val dangerous : Boolean;
private var age : Int;
} extends Cat
~~~
~~~
等同于:
~~~
~~~
class Tiger(param1 : Boolean, param2 : Boolean) extends Cat {
override val dangerous = param1;
private var age = param2;
}
~~~
~~~
这样的写法称之为提前定义,也就是定义参数化字段
~~~
-
调用超类构造器
- 把要传递的参数或参数列表放在超类名之后的括号里即可
-
使用override修饰符
- 若子类成员重写了父类的具体成员则必须带有这个修饰符,若实现的是同名的抽象成员时,则这个修饰符是可选的
- 该规则给编译器提供了便利,可以帮助其避免某些难以捕捉的错误并使得对系统的改进更加安全
-
多态和动态绑定
- 父类的变量可以指向子类的对象,这种现象称之为多态
-
定义final成员
- 确保一个成员不被重写,需要给改成员添加修饰符 final 来实现
-
使用组合与继承
- 如果追求的是根本上的代码重用,通常采用组合而不是继承
';
第九章 控制抽象
最后更新于:2022-04-01 20:15:51
在上一章中学习了函数值的概念,本章将要学习如何将函数值应用到创建新的控制抽象。
-
简化代码
- 所有的函数都可以分为通用部分和非通用部分
- 通用部分是函数体
- 非通用部分必须由参数提供
- 我们将这样的函数称为高阶函数,这使得我们有机会去组织和简化代码
~~~
def containsNeg(nums: List[Int]):Boolean = {
var exists = false;
for(num <- nums)
if(num < 0)
exists = ture;
exists;
}
~~~
~~~
如果我们在这里使用高阶函数exists:
~~~
~~~
def containsNeg(nums: List[Int]):Boolean = nums.exists(_ < 0);
~~~
~~~
就是如此的简洁!
~~~
-
柯里化
-
柯里化的函数被应用于多个参数列表,而不仅仅一个
~~~
def curriendSum(x: Int, y: Int) = {
x + y;
}
~~~
可以写作:
~~~
def curriendSum(x: Int)(y: Int) = {
x + y;
}
~~~
还可以写作:
~~~
def curriendSum(x: Int) = {
(y: Int) => x + y;
}
~~~
-
编写新的控制抽象
- 对于多个参数,先做柯里化
- 在传入一个参数时,可以用花括号代替小括号达到更像控制抽象的效果
~~~
def twice(x: Double, op: Double => Double) = op(op(x));
~~~
~~~
改写:
~~~
~~~
def twice(x: Double)(op: Double => Double) = op(op(x));
~~~
~~~
调用
~~~
~~~
twice(5) {
_ + 1;
}
~~~
~~~
这样看起来就更加像是内建的控制抽象了。
~~~
-
传名参数
- 要实现传名参数,要定义参数的类型开始与 => 而不是 ()=>
- 需要将 ()=> 转换成 =>
- 括号内的表达式不会先于调用函数被评估,而是创建一个函数值,其apply方法将其评估,而这个函数值则会传递给调用函数
';
第八章 函数和闭包
最后更新于:2022-04-01 20:15:49
-
本地函数
- 函数式编程风格的重要设计原则是程序应该被解构成若干个小的函数,但是这些帮助函数的名称可能会污染整个命名空间。
- 解决这样问题的办法: 将小函数设置为私有方法,或者把函数定义在别的函数之内,就如同本地变量一样,这样的函数我们称为本地函数。
-
头等函数
- 不仅可以定义和调用函数,还可以将其写作匿名的字面量,并把它们作为值传递。
- 函数自面量杯编译进类,并在运行期实例化为函数值。很类似于类(源代码)和对象(运行期)的关系
-
函数字面量的缩写和占位符
- => 左右两边有一侧能够推断出类型就能够缩写
- 例如:
~~~
someNumber.filter((x: Int) => x > 0);
someNumber.filter((x) => x > 0);
someNumber.filter(x => x > 0);
someNumber.filter(_ > 0);
~~~
-
闭包
- def makeIncreaser(more: Int) = (x: Int) => x + more;
这里more是个自由变量,而x是绑定变量。
-
重复参数
- 指明参数长度可变。
- _* 这个标注会告诉编译器把其中每个元素当作参数,而不是当作单一的参数传递。
-
尾递归
- 最后一个动作调用自己的函数称为尾递归函数。
- 尾递归具有较大的局限性,因为JVM指令集使实现更加先进的尾递归形式变得很困难。如果递归是间接的就没有优化的可能性了。
';
第七章 内建控制结构
最后更新于:2022-04-01 20:15:47
Scala中的内建控制结构只有:if, while, for, try, match 和函数调用而已。 之所以没有更高级的内建控制结构,是因为Scala从语法层面上支持函数字面量,可以在基本语法上添加高级的内建控制结构。
-
if表达式
- 同其他语言的if语法结构类似
- if是可以返回值的,这里展示一个函数式风格编写的例子:
~~~
val filename = if(!args.isEmpty) args(0); else "default.txt";
~~~
-
while循环
- 与其他语言中的while循环完全一致
- while和do-while之所以被称为“循环”,而不是表达式,是因为他们不能产生有意义的 结果。其结果类型是Unit
-
for表达式
- for能做的最简单的事情就是把集合中的所有元素都枚举一遍
- 例举一个采用发生器语法的例子,file <- filesHere
~~~
val filesHere = (new java.io.File(".")).listsFiles;
for(file <- filesHere) println(file);
~~~
- 使用 “ 1 to n ” 可以创建Range类型,如果不想包含上边界则可以使用 “ 1 until n ”
- 守卫,在for中增加if判断条件
~~~
val filesHere = (new java.io.File(".")).listsFiles;
for(file <- filesHere if file.getName.endsWith(".scala")) println(file);
~~~
- 还可以在for循环中加入多个 <- 子句
- 可以进行流间变量绑定,绑定的变量被当作val引入和使用,但不带关键字val
- 制造新集合,用关键字 yield 使for表达式产生的结果存放在数组中
~~~
def scalaFiles = for (file <- filesHere if file.getName.endsWith(".scala")) yield file
~~~
-
使用try表达式处理异常
1. 抛出异常
Scala中,throw也是有结果类型的表达式
~~~
val half =
if (n % 2 == 0)
n / 2;
else
throw new RuntimeException("n must be even");
~~~
~~~
2. 捕获异常
Scala选择catch子句这种语法的原因是为了与模式匹配保持一致
~~~
~~~
try {
...
}
catch {
case ex: ... => ...
case ex: ... => ...
case ex: ... => ...
}
~~~
~~~
3. finally子句
如果需要让某些代码无论如何终止都要执行时,可以将其放在finally子句里
4. 生成值
和大多数Scala控制结构一样,try-catch-finally也产生值
~~~
-
match表达式
- 类似其他语言的switch
- 可以让你使用任意的模式做选择
- 每个备选项后面并没有break,break是隐含的
- 不再使用break和continue
-
变量范围
- Scala允许在内部范围内创建与外部范围变量同名的变量
- 脱离所在大括号范围时,变量失效
';
第六章 函数式对象
最后更新于:2022-04-01 20:15:44
-
创建类
- 类的声明中就会带有主构造器
- 不可变对象
- 优点
- 没有会随着时间变化的复杂的状态空间
- 可以传递不可变对象
- 没有线程可以改变不可变对象的状态
- 使哈希表更安全
- 缺点
- 有时需要赋值很大的对象表
-
重写方法
- 需要关键字 override def
-
检查先决条件
- 使用require方法
-
添加字段
- 类中定义的参数,使用时,必须要让该参数初始化字段,使用字段达到相应效果
- 添加字段后,可以在外部访问字段
-
自指向
- 关键字 this 指向当前执行方法被调用的对象实例,若在构造器中,就是正被构建的对象实例
-
辅助构造器
- 主构造器之外的构造器称为辅助构造器
- 辅助构造器的定义开始于 def this(…)
- 每个辅助构造器的第一个动作都是调用同类的别的构造器
- 被调用的构造器可以是主构造器也可以是源文件中早于调用构造器定义的其他辅助构造器
-
私有字段和方法
- 私有字段只能在类的主体之内被访问,外部不可见
- 关键字 private 定义的字段和方法都是外部不可见的
-
定义操作符
- 操作符也是函数的一种,可以自己按需求定义
-
Scala的标识符
- 字母数字标识符:以字母或下划线开始,之后可以跟字母、数字或下划线
- 操作符标识符:由一个或多个操作符字符组成,如 +, :, ?, ~, #等
- 混合标识符:由字母数字组成,后面跟着下划线和一个操作符标识符,如unary_+
- 字面量标识符:用反引号 ‘…’ 包括的任意字符串
-
方法重载
-
隐式转换
- 隐式转化将库变得更灵活、更方便
- 用关键字 implicit 定义转换方法
- 要隐式转换起作用就必须定义在作用范围之内
-
告诫
- Scala允许使用者用操作符名称来创建方法并定义隐式转换,便于设计出更简洁和更易理解的库
- 有了大量的设计这种易于使用库的能力, 相应的责任也愈发的大了
- 操作符方法通常会使客户代码更简洁,但它只会在客户程序员能够识别和记住每个操作符的意思的程度上使程序更易读
';
第五章 基本类型和操作
最后更新于:2022-04-01 20:15:42
-
基本类型
- Byte Short Int Long Char String Float Double Boolean
- 和Java中基本类型所对应的范围完全一样
-
字面量
- 字面量就是直接写在代码里的常量值
- 整数自面量
- 十六进制 开始于0x或0X
- 八进制 开始于0
- Long类型 以L或l结尾
- 浮点数自面量
- 如1.2345e1,就是1.2345*10
- Double类型 以D或d结尾
- Float类型 以F或f结尾
- 字符自面量
- 可以是在单引号之间的任何Unicode字符
- 以\开头表示八进制或十六进制
- 以\u开头连接4位十六进制数字表示通用的Unicode字符
- 字符串自面量
- 由双引号包括的字符组成
- Scala为原始字符串引入了三个引号(“”“),以其作为开始和结束,内部可以包含任意字符
- 符号自面量
- 符号字面量被改写成 ‘<标识符>
- 布尔型自面量
- 两个字面量 true false
-
操作符和方法
- 操作符实际是普通方法调用的另一种表现形式
- 任何方法都可以是操作符
-
数学运算、关系和逻辑操作、位操作符
- 与Java中的相同
-
对象相等性
- 比较两个对象是否相等使用 == 或它的反义 !=
- 对于原始类型的比较,和Java一样,对引用类型的比较,Scala提供eq和ne方法
-
操作符的优先级和关联性
- Scala没有操作符,只是方法的一种表达形式
- 根据方法的第一个字符判断优先级(有列外),如: *= 比 + 的优先级要低
-
富包装器
- 每个基本类型都对应着一个“富包装器”提供许多额外的方法
- 如要看基本类型的所有可用方法,还应该看一下每个基本类型的富包装器的API文档
';
第四章 类和对象
最后更新于:2022-04-01 20:15:40
-
类、字段和方法
- 类定义中可以放置字段和方法;
- 方法用关键字 def 定义;
- 字段保留对象的状态或数据;
- Scala里把成员公开的方法是不显示地指定任何访问修饰符。即,Public是Scala的默认访问级别。
- Scala里方法参数的一个重要特征是他们都是val的,不能在函数中对参数进行重新赋值。
-
分号推断规则 除非以下任一情况出现,否则行尾被认为是一个分号
- 疑问行由一个不能合法作为语句结尾的字结束,如句点或中缀操作符;
- 下一行开始于不能作为语句开始的词;
- 行结束于括号或方括号内部,因为这些符号不可能容纳多个语句;
-
Singleton(单例)对象
- 除了用关键字 object 替换了关键字 class 以外,单例对象的定义看上去与类型一一致;
- 如果单例对象与某个类共享同一个名称时,它就被称为是这个单例对象的伴生对象;
- 类和其伴生对象必须定义在同一个源文件里;
- 类被称之为这个单例对象的伴生类;
- 两者之间可以互相访问私有成员;
- 单例对象在第一次被访问时才会被初始化;
-
Scala程序
- 任何带有合适签名的main方法的单例对象都可以作为程序的入口点;
- Scala的基本编译器:scalac;
- 如果文件是以定义结尾的,则不是scala脚本,脚本必须以结果表达式结束;
- Scala发布包中包含一个叫做fsc的Scala编译器后台,可以将文件列表发送给后台程序,由其完成编译,使用fsc只需在首次运行时等待Java运行环境的启动;
- Application特质
- 该特质可以减少一些输入的工作,单例对象 extends App 就可以当作程序入口执行,而不需要写main方法;
- 原因:特质Application声明了带有合适签名的main方法,并被单例对象继承,使之可以像Scala程序那样;
';
第三章 scala入门再探
最后更新于:2022-04-01 20:15:38
- 类型参数化数组
~~~
val arr = new Array[String](3);
arr(0) = "Hello";
arr(1) = ",";
arr(2) = "World";
~~~
~~~
这里的arr(0) = "Hello";将被转化为arr.update(0,"Hello");即函数的调用
~~~
- 使用列表
1. Scala是可变的同类对象序列,而不可变的同类对象序列是列表类;
`val lst = List(1, 2, 3);`
1. 列表本身不可变
1. 提供方法 ::: 实现叠加功能
~~~
val fstList = List(1, 2);
val sndList = List(3, 4);
val trdList = fstList ::: sndList;
println(trdList)
~~~
~~~
结果:
~~~
~~~
List(1, 2, 3, 4)
~~~
提供方法 :: 将新元素组合到现有列表的最前端,产生新列表作为结果返回;
空列表简写为: Nil
-
元组
元组是不可变的;
元组可以包含不同类型的元素;
val pair = (6299, “iPad Pro”);
用方法 _数字(从1开始) 访问元组的组元
pair _1 //结果:6299
为什么不能用访问列表的方法来访问元组?
因为列表的apply方法始终返回同样的类型,但元组里的类型不尽相同。所以访问方法也不一样。
列表的索引脚标是基于0的,而元组则是基于1的(Haskell和ML等含有静态类型元组的语言,从1开始是传统的设定)。
-
集(set)和映射(map)
Scala的集合库区分为可变类型和不可变类型;
-
Set
Scala API 中提供了一个set的基本特质,还提供了两个子特质,分为可变和不可变;
~~~
var jetSet = Set("Scala", "Program");
jetSet += "GO";
~~~
~~~
jetSet是默认的不可变集,初始化为包含了两个字符串的集,创建set的方法和创建list和array的类似,通过其伴生对象的apply工厂方法。
方法 + 对于不可变集来说,会产生一个全新集,对可变集来说则是把元素加入自身。
~~~
-
Map
与set类似;
~~~
import scala.collection.mutable.Map
val map = Map[Int, String]();
map += (1,"one");
map += (2, "two");
~~~
~~~
map也可表示为 (1 -> "one");
键值对,访问map(k)得到相应的value;
~~~
-
函数式风格
多练习Scala集合库中的方法对体会函数式编程风格很有帮助;
崇尚val,不可变对象和没有副作用的方法;
-
从文件中读取文本行
引用scala.io的Source类
方法 getLine 返回Iterator[String]
';
第二章 scala入门初探
最后更新于:2022-04-01 20:15:35
**注意**:Scala中,表达式后的分号可以省去(多个表达式写在同一行则还是需要分号的),但是Scala源码中都是带分号的,所以建议保留分号
- 使用Scala解释器
- 在Scala解释器中键入代码块可以采用命令::paste,以CTRL+D结束;
- 变量定义
- var声明的变量,可以在生命周期中被多次赋值 ;
- val声明的变量,类似Java中final变量,一旦初始化,就不能再被赋值;
- 变量最好写明类型,这会有便于之后的代码阅读(虽然Scala中有类型推断机制);
-
函数定义
- 定义函数用关键字def
- 函数名
- 参数列表,其中每个参数都必须带有前缀冒号的类型标注(编译器无法推断函数的参数类型)
- 函数结果类型,例如(: Int)
- 函数体,放在{}内
~~~
def max(x: Int, y: Int): Int = {
if(x > y) {
x;
} else {
y;
}
}
~~~
-
脚本
使用命令scala可以编译.scala文件;
命令行参数存放在名为args的Scala数组里,用args(0)访问第一个元素;
-
while做循环,if做判断
~~~
var i = 0;
while(i < args.length) {
if(i != 0) {
print(" ");
}//(个人习惯)在代码块仅有一行语句时也用{}
println(args(i));
i += 1;
}
println();
~~~
-
foreach和for做枚举
- 用foreach可以更简洁的打印每个命令行参数,这是一种更为函数式的编程风格。
以下三种写法都表达同一个意思:
-
第三种最简明的写法的条件是:函数字面量只有一行语句并指代一个参数,那么可以将指代参数省去。 `
args.foreach(arg => println(arg));
args.foreach((arg: String) => println(arg));
args.foreach(println);
`
-
函数自面量:
1. 括号及命名参数列表 (x: Int, y: Int)
1. 右箭头 =>
1. 函数体 x + y;
用for也可以写出简明的代码:
~~~
for(arg <- args) {
println(arg);
}
~~~
';
前言
最后更新于:2022-04-01 20:15:33
> 原文出处:[Scala编程学习笔记](http://blog.csdn.net/column/details/learning-scala.html)
作者:[suchang1127](http://blog.csdn.net/suchang1127)
**本系列文章经作者授权在看云整理发布,未经作者允许,请勿转载!**
# Scala编程学习笔记
> 与大家分享一些学习Scala的重点内容及心得。作为一个新手,难免会犯一些错误,希望大家多多指教。
';