Scala语言学习
基础
- 安装
Scala -
REPL(Read-Evaluate-Print-Loop shell) - 处理数据
- 字面量:直接出现在源代码中的数据
- 值
value:val <identifier>[: <type>] = <data>- 不可变的、有类型的存储单元,支持类型推导
- 变量
variable:var <identifier>[: <type>] = <data>- 可变的、有类型的存储单元,支持类型推导
- 可重新赋值,但是不可改变数据类型(支持类型转换的例外)
- 命名:支持使用字母、数字和一些特殊的操作符字符
例如可以使用
等,不能使用
[]和.-
合法命名规则
- 不以数字开头
- 以数字开头的话,编译器最初会把解析到的第一个数字当成一个字面量数字,然后后面解析到其他非数字字符就会出错
- 一个或多个除反引用号外的任意字符,这些字符必须包围在一对反引号之中
val `a.b` = 4 - 不以数字开头
- 类型
-
核心数值类型
-
Byte、Short、Int、Long、Float、Double -
toType方法手动完成类型间转换 - 与
JVM包装数值类型可以互操作
-
-
字符串
String建立在
Java的String基础上与
Java不同,==会检查字符串真正的想等性,而不是对象引用想等性三重引号创建多行
String,类似Python""" your string """-
字符串内插
- 在字符串的第一个双引号前面增加一个
s前缀,使用美元符$指示插入数据的引用(某些容易区分的情况下,大括号{}可以省略,但是加上更容易区分)
val approx = 355/113f println(s"Pi, using 355/133, is about ${approx}.") - 在字符串的第一个双引号前面增加一个
-
字符串内插的替代格式可以使用
printf语法(java.util.Formatter)- 在字符串前面添加
f前缀
scala> val item = "apple" scala> f"Enjoying this $item ${355/133.0}%.5f times today.") - 在字符串前面添加
-
正则表达式(基于
Java类java.util.regex.Pattern)- 用正则表达式捕获值
val <Regex value>(<identifier>) = <input string>) val pattern = """.* apple ([\d.]+) times .*""".rval pattern(amountText) = inputval amount = amountText.toDouble
- 用正则表达式捕获值
-
Scala类型概述
Scala类型概述-
Nothing:所有类型的子类 -
Null:所有指示null值的AnyRef类型的子类 -
Unit:指示没有值(类似void) -
Any:Scala中所有类型的根 -
AnyVal:所有值类型的根 -
AnyRef:所有引用(非值)类型的根
-
Scala不支持其他类型到Boolean类型的自动转换。非null字符串不会计算为true-
元组
(<value 1>,<value 2>[,<value 3>...])- 不同于列表和数组,没有办法迭代处理一个元组中的元素,元组的作用只是作为多个值的容器
- 根据元素的索引访问元组中的元素(下划线加上索引,索引从1开始)
- 创建一个大小为2的元组,也可以使用关系操作符(
->),表示元组中的键值对
-
表达式和条件式
-
表达式块(用
{}结合多个表达式创建,最后一个表达式作为表达式块的返回值)- 可嵌套、跨行,多个表达式写在一行需要
;分隔
- 可嵌套、跨行,多个表达式写在一行需要
语句:不返回值的表达式
-
if...else表达式块if (<Boolean expression>) <expression>if (<Boolean expression>) <expression> else <expression>
-
匹配表达式(类似
switch)-
Scala匹配表达式支持匹配如值、类型、正则表达式、数值范围和数据结构内容
<expression> match{ case <pattern match> => <expression> [case ...] }- 可以使用模式替换式(
pattern alternative),匹配多种内容 case <pattern 1> | <pattern 2> .. => <one or more expressions>- 增加通配模式,放止输入内容无法匹配的情况运行时异常
- 值绑定模式
case <identifier> => <one or more expressions>- 将输入绑定到一个特定的字面量上,如果不匹配就会进入这个绑定值的情况
- 使用通配符
_:相当于匿名占位符,将在运行时替换为一个表达式的值- 与值绑定的不同在于不能再
case表达式块中访问通配符的值
- 与值绑定的不同在于不能再
- 值绑定模式
- 用模式哨位匹配
- 增加一个
if语句,为匹配表达式增加条件逻辑 case <pattern> if <Boolean expression> => <one or more expression>
- 增加一个
- 用模式变量匹配类型
- 匹配输入表达式的类型
case <identifier>: <type> => <one or more expressions>
-
-
循环
-
Range(范围)数据结构<starting integer> [to|until] <ending integer> [by increment]
-
基本for循环迭代处理
for (<indentifier> <- <iterator>) [yield] [<expression>]-
yield:如果指定,则调用的所有表达式的返回值将作为一个集合返回,如果没有指定,则调用表达式,但是不能访问它的返回值 for (x <- 1 to 7){println(s"Day $x:")}
-
迭代器哨位(过滤器)
for (<indentifier> <- <iterator> if <Boolean expression>)...threes = for(i <- 1 to 20 if i%3 == 0) yield i
-
嵌套迭代器
for{ x <- 1 to 2 y <- 1 to 3 } { print(s"($x,$y) ")} -
值绑定
for (<indentifier> <- <iterator>; <identifier> = <expression>)...for(i <- 0 to 8; pow = 1 <<i) yield pow
-
While和Do/While循环while(<Boolean expression>) statement
-
函数
- 定义无输入的函数
def <identifier> = <expression>def <identifier>() = <expression>- 如果定义函数时没加小括号,则调用时也不可以加小括号,而加了小括号的无输入函数调用时可以不加小括号
- 定义函数时指定返回类型
def <identifier>: <type> = <expression> - 定义函数
def <identifier>(<identifier>: <type>[,...]): <type> = <expression> - 过程:没有返回值的函数
- 使用表达式块调用函数
- 当使用一个参数调用函数时,可以使用表达式块返回的结果发送参数
- 递归函数
- 递归函数可能会遇到“栈溢出”错误,
Scala编译器可以使用尾递归优化一些递归函数 - 利用尾递归优化的函数,递归调用不会创建新的空间,而是使用当前函数的栈空间
- 只有最后一个语句是递归调用的函数才能由
Scala编译器完成尾递归优化 - 可以利用函数注解来标志一个函数将完成尾递归优化
@annotation.tailrec
- 递归函数可能会遇到“栈溢出”错误,
- 嵌套函数
- 用命名参数调用函数
- 可以不按原先定义的顺序指定参数
- 有默认值的参数
-
Vararg参数- 这是一个函数参数,可以匹配调用者的0个或多个实参
-
vararg参数后面不能跟非vararg参数,因为无法加以区分 -
vararg参数实现为一个集合,可以通过迭代器访问 - 要标志一个参数匹配一个或者多个输入实参,在函数定义中需要该参数类型后面增加一个
*号 - 例如
def sum(items: Int*): Int = {...}
- 参数组
- 可以把参数表分解为参数组,每个参数组用小括号分割
- 例如
def max(x:Int)(y:Int) = if (x > y) x else y
- 类型参数
- 类型参数指示了值参数类型或返回值类型
- 函数参数或返回值的类型不再固定,而是可以由函数调用者设置
def <function-name>[type-name](<parameter-name>:<type-name>): <type-name>...- 例如
def identity[A](a: A): A = a
- 方法和操作符
- 中缀点记法:
<class instance>.<method>[(<parameters>)] -
Scala中使用的所有算术运算符都是方法 -
Scala的操作符记法允许使用空格分隔对象、操作符方法和方法的参数(只有一个) - 例如
2 + 3和2.+(3)是一样的 - 操作符记法:
<object> <method> <parameter>- 如果要调用多个参数,则必须将多个参数包含在
()中
- 如果要调用多个参数,则必须将多个参数包含在
- 中缀点记法:
首类函数
- “首类”表示函数不仅能得到声明和调用,还可以作为一个数据结构用在这个语言的任何地方
- 高阶函数:接受其他函数作为参数,或者使用函数作为返回值
- 声明式编程:要求使用高阶函数或其他机制声明要做的工作,而不手动实现
- 命令式编程:总要明确指定操作的逻辑流
- 函数类型和值
函数的类型是其输入类型与返回值类型的一个简单组合,用一个箭头从输入类型指向输出类型
-
([<type>, ...]) => <type>def double(x: Int): Int = x*2 val myDouble: (Int) => Int = double val myDoubleCopy = myDouble val myDouble2 = double _-
myDouble就是一个值,只不过这个值可以调用 - 一个函数值可以赋值给一个新值
-
myDouble必须有显示的类型,以区分它是一个函数值,而不是一个函数调用(如果函数只有单个参数可以省略小括号) -
val <identifier> = <function name> _:使用通配符为函数赋值
-
- 函数字面量
- 创建一个没有名字的函数并把这个函数赋值一个新的函数值
val doubler = (x: Int) => x * 2- 函数字面量是所赋数据的一个字面量表达式
- 其他名字:
匿名函数、Lambda表达式、Lambdas、function0,function1,function2,...(Scala编译器对字面量的叫法。根据输入参数的个数而定,单参数就叫<function1>) - 语法:
([<identifier>: <type>, ...]) => <expression>
- 占位符语法
函数字面量的一种缩写形式,将命名参数替换为通配符(
_)-
使用条件
- 函数的现实类型在字面量之外指定(明确好输入输出)
- 输入参数最多只使用一次(因为只能按照位置匹配一次,多个占位符有着相同的符号,匹配之后就无法确定需要的参数)
-
示例
def combination(x: Int, y: Int, f: (Int,Int) => Int) = f(x,y) combination(23, 12, _*_)def tripleOp[A,B](a: A, b: A, c: A, f: (A, A, A) => B) => f(a, b, c) tripleOp[Int, Int](23, 92, 14, _ * _ + _) tripleOp[Int, Double](23, 92, 14, 1.0 * _ / _ / _)
- 部分应用函数和柯里化
功能:为函数保留一些重复使用的参数
-
方法一:部分应用(
partially apply)函数,使用通配符替代其他参数(函数按位置匹配)def factorOf(x: Int, y: Int) = y % x == 0 val mutipleOf3 = factorOf(3, _: Int) val y = mutipleOf3(78) -
方法二:函数柯里化(
curring,讨好的意思),使用参数组-
有多个参数表的函数可以认为是多个函数的一个链。单个参数表则认为是一个单独的函数调用
def factorOf(x: Int)(y: Int) = y % x == 0 val isEven = factorOf(2) _ val z = isEven(32) // z = true def factorOf(x: Int)(y: Int)的函数类型为Int => Int => Boolean
-
- 传名参数
一种函数参数类型:可以取一个值,也可以取最终返回一个值的函数
-
语法:
<identifier>: => <type>scala> def doubles(x: => Int) = { | println("Now doubling" + x) | x * 2 | } doubles: (x: => Int)Int scala> doubles(5) Now doubling 5 res1: Int = 10 scala> def f(i: Int) = { println(s"Hello from f($i)"); i } f: (i: Int)Int scala> doubles( f(8) ) Hello from f(8) Now doubling 8 Hello from f(8) //doubles调用两次x,所以f函数也调用两次 res2: Int = 16
- 偏函数
- 有些函数并不能支持满足输入类型的所有可能值。这种函数称为偏函数,因为它们只能部分应用于输入数据
- 用函数字面量块调用高阶函数
- 实际上就是输入的参数类型是函数,那个输入参数可以用表达式块或者函数字面量来代替
- 好处
- 将单独的代码块包为在工具函数中
- 管理数据库事务,即高阶函数打开会话,调用函数参数,然后commit或者rollback结束事务
- 重新尝试处理可能的错误,将函数参数调用指定的次数,直到不再产生错误
- 根据局部、全局或者外部值有条件地调用函数参数
常用集合
- 列表、集和映射
-
List:不可变的单链表- 访问列表中的单个元素,可以作为一个函数调用这个列表,并提供一个索引号(从0开始)
- List是一个不可变的递归数据结构(链表),列表中的每一项都有自己的表头和越来越短的表尾
- 所有列表都有一个
Nil实例作为终结点,可以通过比较当前元素与Nil来检查是否到达列表表尾(Nil实际上是List[Nothing]的一个单例实例)
-
Set:不可变的无序集合 -
Map:不可变的键值库,也称为散列映射、字典或关联数组 -
List、Set、Map的根类型都是Iterable-
foreach():取一个函数,对列表中的每一项,分别调用这个函数 -
map():取一个函数,将一个列表元素转换为另一个值或类型 -
reduce():取一个函数,将两个列表元素结合为一个元素
-
-
-
Cons操作符可以通过
cons(construct的简写)操作符来构建列表-
使用
Nil为基础,并使用右结合的cons操作符::绑定元素,就可以构建列表val numbersOld = List(1,2,3) val numbers = 1 :: 2 :: 3 :: Nil -
::是List的一个方法,它取一个值,这会成为新列表的表头val first = Nil.::(1) // 1 -> Nil
- 列表算术运算
- 因为
List是一个不可变的集合,所有“修改”是指“返回一个新列表,其中包含所请求的修改” - 谓词函数(
predicate function):得到一个输入值后会相应地返回true或false -
:::为列表追加单个元素 -
::::为列表追加另一个列表 -
++:为列表追加另一个集合 -
==:判断集合类型和元素内容是否相同 -
distinct:返回不包含重复的列表 -
drop:去除列表前n个元素 -
filter:输入一个谓词函数,过滤掉指定的元素 -
flatten:将一个包含列表的列表转换为元素列表 -
partition:输入一个谓词函数,将元素分组,由两个列表组成一个元组 -
reverse:逆转列表 -
slice:切片,输入切片的开头和结尾,输出不包括结尾 -
sortBy:根据指定函数排序 -
sorted:根据自然顺序排序 -
splitAt:根据指定索引将列表切分成两个 -
take:从列表抽取前n个元素 -
zip:两个列表合并为一个元组列表
- 因为
- 映射列表
-
collect:使用偏函数转换各个元素,保留可应用的元素 -
flatMap:给定一个函数转换各个元素,扁平化结果(消除列表中的列表,这种嵌套结构) -
map:使用给定函数转换各个元素 -
Scala可以把Java数组转换为它自己的类型Array
-
- 归约列表
- 将列表收缩为单个值
-
Scala的集合支持:- 数学归约:
max、min、product、sum - 逻辑归约:
contains、endsWith、exists、forall、startsWith - 通用的高阶操作,折叠,可以用来创建任何其他类型的列表归约算法
-
fold、foldLeft、foldRight、reduce、reduceLeft、reduceRight、scan、scanLeft、scanRight
-
- 数学归约:
- 转换集合
-
mkString:根据指定分隔符将一个集合转换为String -
toBuffer:将一个不可变的集合转变为可变的集合 -
toList:将一个集合转变为List -
toMap:将一个2元元组的集合转换为一个Map -
toSet:将一个集合转换为一个Set -
toString:将一个集合呈现为一个String,包括集合的类型
-
-
Java和Scala集合兼容性- 默认情况下两者集合类型是不兼容的
- 添加
JavaConverters:import collection.JavaConverters._ - 集合转换:
asJava、asScala
- 使用集合的模式匹配
- 匹配表达式、模式哨卫、值绑定
- 模式匹配是
Scala语言的一个核心特性
更多集合
-
可变的集合类型 : 不可变的集合类型
-
collection.mutable.Buffer:collection.imutable.List -
collection.mutable.Set:collection.imutable.Set -
collection.mutable.Map:collection.imutable.Map - 使用
toList、toSet、toMap将可变集合类型转换回不可变的集合类型
-
-
从不可变集合创建可变集合
- 不可变集合
List、Map和Set都可以使用toBuffer方法转换为可变的collection.mutable.Buffer类型
- 不可变集合
-
使用集合构建器
-
Builder:生成指定的集合类型,只支持追加操作 Set.newBuilder[Char]- 需要调用
result方法得到最终结果
-
-
数组
- Array是一个大小固定的可变索引集合,实际只是Java数组类型的一个包装器,另外还提供了隐含类高级特性,使它可以项序列一样使用
val numbers = Array(1, 2, 3, 4)
-
Seq和序列-
Seq是所有序列的根类型
序列集合层次体系 Seq:所有序列的根类型,List()的快捷方式IndexedSeq:索引序列的根类型,Vector()的快捷方式Vector:这一类列表有一个后备Array实例,可以按索引访问Range:整数范围。动态生成数据LinearSeq:线性(链表)序列的根类型List:元素的单链表Queue:先进先出列表Stack:后进先出列表Stream:懒列表。访问元素时才增加相应元素String:字符集合,扩展了Iterable
-
-
Stream懒集合,由一个或多个启示元素和一个递归函数生成。流可能是无界的,理论上是无限的集合,只是在访问元素时才回生成这个元素
使用
Stream.cons用表头表尾构建一个新的流,替代语法,可以使用#::操作符示例:
def inc(head: Int): Stream[Int] = head #:: inc(head+1)-
创建一个有界的流
def to(head: Char, end: Char): Stream[Char] = (head > end) match { case true => Stream.empty case false => head #:: to((head+1).toChar, end) }
-
一元集合
- 支持类似
Iterable中的变换操作,但是包含的元素不能多于1个
- 支持类似
-
Option集合- 扩展了
Iterable的一个一元集合,Option类型表示一个值的存在或不存在 -
Option可以安全地替代null值,告诉用户这个值可能不存在,从而减少出发NullPointerException异常的可能性 - 另一些开发人员把它看作是构建操作链的一种更为安全的方法,确保操作链中只包含有效的值
-
Option依赖两个子类型提供的具体实现:Some和None-
Some:一个类型参数化的单元素集合 -
None:一个空集合。None类型没有类型参数,因为它永远不包含任何内容 - 用
isDefined和isEmpty分别检查一个给定的Option是Some还是None
-
-
Option集合提供了一种安全的机制,而且还提供了一些操作来存储和变换可能存在也可能不存在的值,还提供了一些安全操作可以抽取可能存在的值 - 警告:避免使用
Option.get(),此方法不安全,对None调用get()将导致运行时错误 - 安全的
Option抽取操作fold-
getOrElse:为Some,则返回值;为None,则返回传名参数的结果 -
orElse:非空,返回这个Option,否则从给定的传名参数返回一个Option - 匹配表达式
match{ case Some(x) => x; case None => -1 }
- 扩展了
-
Try集合- 将错误处理转变为集合管理。提供一种机制来捕获给定函数参数中发生的错误,并返回这个错误。
-
Scala抛出一个异常会中断程序流,并把控制交回给最近的处理器来处理这个特定的异常。未处理的异常会终止应用。 -
Scala支持try{}..catch{}块。 - 推荐使用
util.Try(),因为它提供了一种更安全、更有表述性,而且纯粹一元的方法来处理错误 -
util.Try类型没有具体实现,但是有两个已实现的子类型Success和Failure - 使用Try的错误处理方法
flatMap:对于Success,调用一个同样返回util.Try的函数,从而将当前返回值映射到一个新的内嵌返回值foreach:一旦Success,执行给定的函数,Failure则什么都不做getOrElse:返回Success中的内嵌值,对于Failure则返回传名参数的值orElse:与flatMap相反。对于Failure,调用一个同样返回util.Try的函数toOption:将util.Try转换为Option,缺点是丢失内嵌的Exceptionmap:对于Success,调用一个函数,将内嵌值映射到一个新值匹配表达式
match { case util.Success(x) =>x; case util.Failure(error) => -1 }-
什么都不做:只需要允许异常在调用栈中向上传播,直到被捕获或者导致当前应用退出
val input = " 123 " val result = util.Try(input.toInt) orElse util.Try(input.trim.toInt) result foreach { r => println(s"Parsed '$input' to $r!") } val x = result match { case util.Success(x) => Some(x) case util.Failure(ex) => { println(s"Couldn't parse input '$input'") None } }
-
Future集合-
concurrent.Future,发起一个后台任务 -
future表示一个可能的值,并提供了安全操作来串链其他操作或者抽取值。与Option和Try不同,future的值不是立即可用,因为创建future时后台任务可能仍在工作。 - 支持在并发线程中运行后台任务。
- 调用
future并提供一个函数会在一个单独的线程中执行该函数,而当前线程仍继续操作 - 在创建
future之前,必须指定当前会话或应用的“上下文”来并发运行函数,默认使用global上下文import concurrent.ExecutionContext.Implicits.global
- 可以设置回调函数或另外的
future,当future任务执行完成时,执行这个回调或者future - 异步处理
future-
fallbackTo:将第二个future串链到第一个future,如果第一个future不成功,则调用第二个future -
flatMap:如果第一个成功,则使用第一个的返回值调用第二个future -
map:将给定的函数串链到future,如果future成功,则其返回值将用来调用这个函数 -
onComplete:future任务完成后,将用一个util.Try调用指定函数,其中包含一个值(成功)或者一个异常(失败) -
onFailure:如果future任务抛出一个异常,将使用这个异常来调用指定函数 -
onSuccess:如果future任务成功完成,将使用返回值来调用给定函数 -
Future.sequence:并发地运行给定序列中的future,返回一个新的future
-
- 同步处理
future-
concurrent.Await.result(),取后台线程和一个最大等待时间 - 等待时间:
import concurrent.duration._ - 等待10s:
val maxTime = Duration(10, SECONDS)
-
-
类
-
基础
- 在
Scala中,类参数在类名后指定,就像函数定义中函数参数跟在函数名后面一样。类参数可以用来初始化字段(类中的值和变量),或者用于传入函数,但是一旦类已经创建,这些参数就不再可用 - 可以把某个字段声明为类参数,通过在类参数前增加
val或者var,类参数就成为类中的一个字段 -
Scala中,一个类可以使用extends关键字扩展最多一个其他类,另外可以用override关键字覆盖所继承方法的行为 - 类中的字段和方法用
this关键字访问,父类中的字段和方法可以用super关键字访问 -
Scala多态指类能够采用其他兼容类的形式。“兼容”指一个子类的实例可以用于替代其父类的实例,但是反过来不行
- 在
-
定义类
class <identifier> [type-parameters] [([val|var] <identifier>: <type>[, ...])] //输入参数可以有默认值 [extends <identifier>[type-parameters](<input parameters>)] //继承父类可以有输入参数 [{ fields, methods, and classes }]- 实例就是一个内存分配,提供了类字段的存储空间。这个动作(即预留空间来分配一个类的内容)称为实例化
- 访问类的字段和方法:标准中缀点记法、中缀操作符记法
-
抽象类
- 将由其他类扩展的一个类,而自己不能实例化
- 由
abstract关键字指定 - 定义其子类必须有的核心字段和方法,但一般不提供具体实现(可以有默认实现)
- 基于多态,如果一个值的类型为抽象类,它可以具体指向某个非抽象子类的实例,调用方法时实际上最后会在子类上调用
重载方法
-
apply方法- 名为“
apply”的方法有时是指它要作为一个默认方法或一个注入方法,可以直接调用而不需要方法名 - 实际是一种快捷方式,可以使用小括号触发功能而不需要方法名,就如
python的__call__方法,以及pytorch的forward
- 名为“
-
懒值
- 定义一个值时可以在
val关键字前面加上关键字lazy来创建一个懒值 - 类中使用的字段(值和变量)都是在类第一次实例化时创建的,而懒值是第一次调用时创建
- 要确保时间或者性能敏感操作在类的生命周期中只执行一次,懒值则是一种很好的方法
- 常用于存储基于文件的属性、打开的数据库链接,以及其他只有在确实必要时才初始化的不可变数据等信息。
- 定义一个值时可以在
-
包装
- 为
Scala文件定义包:package <identifier> - 源文件要存储在与包匹配的目录中
- 访问包装类:用点分隔的包路径或者导入包
- 与
Java不同,代码中的任何位置都可以使用import,与python类似 - 支持使用
_操作符导入一个包的全部内容 - 导入组:
import <package>.{<class 1>[,<class 2>...]} - 使用导入别名:
import <package>.{<original name> => <alias>} - 包装语法:包作为一个块,用大括号包围它的类
package <identifier> { <class definitions> }
- 为
-
私密性控制
- 默认地,
Scala不会增加私密性控制 -
protected:同一个类或者子类中的代码才能访问 -
private:仅定义这个字段或方法的类可以访问
- 默认地,
-
私密性访问修饰符
- 包级保护
- 另外,可以根据另一个类的亲密性覆盖这些策略
- 实例级保护
- 包级保护
-
最终类和密封类
-
final类不能在子类中被覆盖,final关键字定义的值,变量或方法,可以确保所有子类都将使用这个实现 -
sealed密封类- 密封类会限制一个类的子类必须位于福勒所在的同一个文件中
-
对象、Case类和Trait
- 对象
object- 一种类类型,只能有不超过1个实例,也就是单例(
singleton) -
Java和其他语言的某些字段和方法为“静态”或“全局”,对象可以提供类似的功能,不过将它们与可实例化的类解耦合 - 对象不是用
new关键字创建,只需要按名字直接访问,在首次访问时自动实例化(第一次访问前,不会实例化) - 定义对象用
object关键字,没有任何输入参数,对象不能扩展,但是别的类可以继承它 - 定义:
object <identifier> [extends <identifier>] [{ fields, methods, and classes }] - 最适合
object的函数是纯函数和处理外部I/O的函数- 纯函数:会返回完全由其输入计算得到的结果,而没有任何副作用
-
I/O函数:处理外部数据的函数,如处理文件、数据库和外部服务
-
apply方法和伴生对象工厂模式是一种很流行的方法,可以从伴生对象生成一个类的新实例。
-
伴生对象是与类同名的一个对象,与类在同一个文件中定义
class Multiplier(val x: Int) { def product(y: Int) = x * y } object Multiplier { def apply(x: Int) = new Multiplier(x) }
- 使用对象的命令行应用
- 使用对象中的一个
main方法作为应用的入口点 def main(args: Array[String]) { ... }
- 使用对象中的一个
-
Case类case class <identifier> ([var] <identifier>: <type>[,...]) [extends <identifier>(<input-parameters>)] [{ fields and methods }]- 不可实例化的类,包含多个自动生成的方法,包括一个自动生成的伴生对象
- 适合数据传输对象,主要用于存储数据
-
val关键字可以用,但是case类默认将输入参数转换为值字段,所以没必要添加,如果需要一个变量字段,则使用var关键字 -
case类方法-
apply:对象object中,一个工厂方法,用于实例化case类 -
copy:类中,返回实例的一个副本 -
equals:类中 -
hashCode:类中 -
toString:类中 -
unapply:对象中,将实例抽取到一个字段元组,从而可以使用case类实例完成模式匹配
-
-
Trait-
trait不能实例化,支持多重继承的类 -
trait不能有类参数,但可以有类型参数 - 定义:
trait <identifier> [extends <identifier>] [{ fields, methods, and classes }] - 用with关键字为类增加一个或多个
trait,类没有扩展trait,而是被trait扩展,trait为现有的类增加新的功能或者配置,这个特性也称为依赖注入-
Spring通过定制Java注解和初始化模块实现了类似的功能,但是Scala的trait不要求特定的注解或特殊的包来实现依赖注入
-
-
- 导入实例成员
- 与导入类和对象的
import使用方法一样,可以导入单个成员,或者通过通配符_导入所有成员 - 不会覆盖私密性控制,只能导入可以正常访问的字段和方法
- 与导入类和对象的
- 一种类类型,只能有不超过1个实例,也就是单例(
高级类型
- 暂略

