变量(variable),在Scala中,有可变、不可变之分:
- var,可变变量,变量(variable),在程序运行过程中其值可能发生改变,适用于OO;
- val,不可变变量,value(值),可理解为常量(final),在程序运行过程中其值不会发生变化,适用于FP。
【定义/声明】变量
注意:
1)变量(不管是var、还是val),定义时必须初始化。否则报错!
scala> var a:Int
<console>:7: error: only classes can have declared but undefined members
(Note that variables need to be initialized to be defined)
var a:Int
^
scala> val a:Int
<console>:7: error: only classes can have declared but undefined members
val a:Int
^
scala> var a:Int = 1
a: Int = 1
scala> var a:String = "1"
a: String = 1
上述报错提示:只有类才能声明,而不定义成员。
2)val变量,一旦初始化,就不可再被赋予新值,即 无法再改变它的值了。
scala> val a = 123 //定义一个val变量
a: Int = 123
scala> a = 456 //val变量一旦初始化,就不可再被赋值,会报错!reassingnment重新分配
<console>:8: error: reassignment to val
a = 456
^
scala> val a = 456 //这表示又定义了一个变量名同为a的局部val变量。而不是重新赋值,因为重新赋值时 变量名前是不带val关键字的
a: Int = 456
3)var变量,在其初始化后,在它的生命周期内,可被多次重新赋值。
scala> var a = 789 //定义var变量
a: Int = 789
scala> a = 100 //这是重新赋值。因为变量名前没有var关键词
a: Int = 100
scala> a
res6: Int = 100
scala> a = 10086 //这还是重新赋值。因为变量名前没有var关键词
a: Int = 10086
scala> a
res8: Int = 10086
4)在定义变量时,数据类型是可选的(不显示指定数据类型),但却在上述结果中显示了具体的数据类型Int
。这是因为Scala自身有数据类型推断能力/机制,Scala编译器可自动理解 省略的具体数据类型,即根据值的类型来推断出变量的所属类型。当然,为了代码的可读性,可自行添加变量数据类型的声明。
scala> val str:String = "不可变变量"
str: String = 不可变变量
scala> var num:Double = 10
num: Double = 10.0
5)关于变量名,可遵循以下几个原则:
- 以下划线或其他符号、字母开头(但不要使用
$
开头,因为以$
开头的标识符为保留的Scala编译器产生的标志符使用),后接字母、数字或符号; - 对作用域较短的变量使用短名字:如
is
、js
、ks
等可用于循环中; - 对作用域较长的变量使用长名字,像那种无需加以说明便可理解的名字。另外,使用外部APIs应该用长的,如
Future.collect
,而不是Future.all
。 - 避免使用保留字;
- 不要在不同用途,重用相同的名字;
- 使用通用的缩写,避开隐秘难懂的缩写。
scala> val empty_? = true //以下这些变量名在Scala中都是合法的
empty_?: Boolean = true
scala> val _s = "abc"
_s: String = abc
scala> val + = "plus"
+: String = plus
scala> val `yield` = 100
yield: Int = 100
scala> val ** = "star"
**: String = star
scala> val name = "lilei"
name: String = lilei
6)变量花式定义
// 赋值为 _,表示缺省值(如0、0.0、nul、false等)
scala> var d:Double = _
d: Double = 0.0
scala> var i:Int = _
i: Int = 0
scala> var s:String = _
s: String = null
scala> val x,y = 0 //两个变量赋同一个初始值
x: Int = 0
y: Int = 0
scala> val x,y:Int = 1
x: Int = 1
y: Int = 1
scala> val x,y = (10, "lily") //给两个变量赋同一个初始值,值是个数组。注意下方赋不同的初始值不一样:同时定义多个变量
x: (Int, String) = (10,lily)
y: (Int, String) = (10,lily)
scala> val (x,y) = (10, "Lily")
x: Int = 10
y: String = Lily
scala> val x,y=10,"Lily" //这是非法的定义
<console>:1: error: ';' expected but ',' found.
val x,y=10,"hello"
^
scala> val x::y = List(1,2,3,4) //两个英文冒号 表示一个普通元素与一个List的连接操作。即List(1,2,3,4)中的普通元素 1赋值给变量x,List(2,3,4)赋值给变量y
x: Int = 1
y: List[Int] = List(2, 3, 4)
scala> val List(a,b,c) = List(1,2,3)
a: Int = 1
b: Int = 2
c: Int = 3
scala> val List(a, b, _, _, c @ _*) = List(1, 2, 3, 4, 5, 6, 7) //也可以用Array、Seq
a: Int = 1
b: Int = 2
c: Seq[Int] = List(5, 6, 7)
scala> val regex = "(\\d+)/(\\d+)/(\\d+)".r //使用正则表达式定义变量
regex: scala.util.matching.Regex = (\d+)/(\d+)/(\d+)
scala> val regex(year, month, day) = "2019/05/07"
year: String = 2019
month: String = 05
day: String = 07
7)lazy
、val
、def
三者区别
- val,在定义的同时,就完成求值,并保持不变
scala> val t = System.currentTimeMillis //马上求值
t: Long = 1557236549316
scala> System.currentTimeMillis
res5: Long = 1557236561174
scala> t //保持不变
res6: Long = 1557236549316
- 定义变量时加上
lazy
关键字,作用是 实现延迟执行,只有在使用时才会执行初始化
scala> lazy val t = System.currentTimeMillis
t: Long = <lazy>
scala> System.currentTimeMillis //此时还没使用变量 t
res7: Long = 1557236756473
scala> t //正在使用
res8: Long = 1557236761901
scala> import scala.io.Source._
import scala.io.Source._
scala> fromFile("/usr/local/src/lianxi_scala/HelloWorld.scala").mkString //读取某个文件
res29: String =
"object HelloWorld {
/*这是第一个scala程序
*改程序将输出Hello world
*/
def main(args: Array[String]) {
println("Hello,world!!")
}
}
"
scala> val info = fromFile("/usr/local/src/lianxi_scala/HelloWorld.scala").mkString //直接输出了!!
info: String =
"object HelloWorld {
/*这是第一个scala程序
*改程序将输出Hello world
*/
def main(args: Array[String]) {
println("Hello,world!!")
}
}
"
scala> lazy val info = fromFile("/usr/local/src/lianxi_scala/HelloWorld.scala").mkString //不使用它时,是不会直接读出内容的。只有在第一次使用时,读取、真正访问。
info: String = <lazy>
scala> lazy val info = fromFile("/usr/local/src/lianxi_scala/HelloWorld.scala222").mkString //而如果我不小心写错文件名或其他,这是不会报错的!!
info: String = <lazy>
scala> info // 真正执行时才会报错!而不使用lazy将会立刻报错。
java.io.FileNotFoundException: /usr/local/src/lianxi_scala/HelloWorld.scala222 (No such file or directory)
at java.io.FileInputStream.open0(Native Method)
...
这种延迟执行的操作,在编程中有实际意义(视情况而定,有利有弊),比如:为了缩短模块启动时间,可以将当前不需要的某些工作推迟执行;保证对象中其他字段的初始化优先执行;打开一个数据库连接,一般希望只有在使用其引用时才初始化,因为对于程序来说,执行这个操作,可能代价是昂贵的。
def
,这是定义一个函数/方法,本质和前面不一样。定义时不求值,不过每次使用时都会重新求一次值。
scala> def t = System.currentTimeMillis //定义函数/方法
t: Long
scala> t //每次求的值都不一样
res11: Long = 1557237041648
scala> t //每次求的值都不一样
res12: Long = 1557237042927
scala> def f = 1+2 //定义函数
f: Int
scala> f //虽然求的值一样,但却是重新计算了一次
res13: Int = 3
scala> f
res14: Int = 3