第三章 Scala基础——变量定义与基本类型

一、定义一个变量

Scala在首次定义一个变量时,必须在变量名前面添加关键字“var”或者“val”。用“var”修饰的变量,可以重新赋予新的值,并且把原值抛弃,类似于Java的非final变量。在后续重新赋值时,就不用再写“var”了。而用“val”修饰的变量,则禁止被重新赋值,类似于Java的final变量,换句话说,就是只能读不能写的变量。

变量名可以是任意的字母、数字和下划线的组合,但是不能以数字开头。Scala推荐的命名方法是“驼峰命名法”,即每个单词的首字母大写,并且变量名和函数名以小写字母开头,类、对象和特质则以大写字母开头。例如,“val isOne”,“class MyClass”。在首次定义变量时,就必须赋予具体的值来初始化。不能出现如下形式:

val x

x = 1

以下代码都是合法的变量定义:

scala> val x = 1
x: Int = 1

scala> var y = 2
y: Int = 2

scala> val msg = "Hello, world!"
msg: String = Hello, world! 

var类型的变量在重新赋值时,新值必须和旧值是同一个类型,否则会发生类型匹配错误:

scala> var x = 1
x: Int = 1

scala> x = 10
x: Int = 10

scala> x = "abc"
<console>:12: error: type mismatch;
 found   : String("abc")
 required: Int
       x = "abc"
           ^

val类型的变量则直接禁止重新赋值:

scala> val x = 1
x: Int = 1

scala> x = 10
<console>:12: error: reassignment to val
       x = 10
         ^

如果要赋给多个变量相同的值,那么没必要逐条定义,而是在一条语句里用逗号间隔的变量名。例如:

scala> val a, b, c = 1
a: Int = 1
b: Int = 1
c: Int = 1

Scala的变量定义具有覆盖性,也就是说,如果出现了同名的变量,则后出现的变量会覆盖前面的变量。例如:

scala> val x = 1
x: Int = 1

scala> val x = 10
x: Int = 10

scala> x
res0: Int = 10

要注意的是,赋给变量的对象存在可变与不可变之分。要理解到底是变量指向的对象本身发生了改变,还是变量指向了新的对象。即使是val类型的变量,也能被赋予一个可变对象。这个可变对象能够被重新修改,例如,给可变映射添加新的键值对。事实上,这只是旧对象发生了改变,并未产生新的对象。

Scala提倡定义val类型的变量,因为它是函数式编程,而函数式编程的思想之一就是传入函数的参数不应该被改变。所以,在Scala里,所有函数的参数都必须是val类型的。但是,Scala也允许指令式编程,因而预留了var类型的变量,尽管并不推荐使用。对于习惯了指令式编程的读者,例如,喜欢编写“for(i = 0; i < N; i++)”来实现一个循环,很显然更倾向于使用var类型的变量,因为在这个for循环里,变量i被多次重新赋值。Scala推荐读者学会使用val,学会函数式编程。笔者也是学习C/C++出身的,但是现在已经完全习惯了函数式编程。使用val类型的一个好处就是,你不用去计算某个变量在某个时刻是什么值,因为val类型的变量一旦被初始化,就一直不变,直到被重新定义。

二、Scala的基本类型

Scala是静态语言,在编译期间会检查每个对象的类型。对于类型不匹配的非法操作,在编译时就能被发现。对于动态语言而言,这种非法操作需要等到运行时才能被发现,此时可能造成严重错误。所以,静态语言相比诸如Python这样的动态语言在某些方面是有优势的。对于Chisel而言,我们就需要这种优势。因为Chisel需要编译成Verilog,我们不能产生非法的Verilog语句并且等到模块运行时才去发现它。

Scala标准库定义了一些基本类型,如下表所示。除了“String”类型是属于java.lang包之外,其余都在Scala的包里。

Scala的基本类型
Byte8-bit有符号整数,补码表示,范围是 -2^{7} 到 2^{7}-1
Short16-bit有符号整数,补码表示,范围是 -2^{15} 到 2^{15}-1
Int32-bit有符号整数,补码表示,范围是 -2^{31} 到 2^{31}-1
Long64-bit有符号整数,补码表示,范围是 -2^{63} 到 2^{63}-1
Char16-bit字符,Unicode编码,范围是 0 到 2^{16}-1
String字符串
Float32-bit单精度浮点数,符合IEEE 754标准
Double64-bit双精度浮点数,符合IEEE 754标准
Boolean布尔值,其值为true或者false

事实上,在定义变量时,应该指明变量的类型,只不过Scala的编译器具有自动推断类型的功能,可以根据赋给变量的对象的类型,来自动推断出变量的类型。如果要显式声明变量的类型,或者无法推断时,则只需在变量名后面加上一个冒号“ : ”,然后在等号与冒号之间写出类型名即可。例如:

scala> val x: Int = 123
x: Int = 123

scala> val y: String = "123"
y: String = 123

scala> val z: Double = 1.2
z: Double = 1.2

   Ⅰ、整数字面量

整数有四种类型,默认情况下推断为Int类型。如果字面量的结尾有l或者L,则推断为Long类型。此外,Byte和Short则需要定义变量时显式声明。注意,赋给的字面值不能超过类型的表示范围。

整数字面量默认是十进制的,但如果以“0x”或者“0X”开头,则字面量被认为是十六进制。十六进制的字母不区分大小写。例如:

scala> val a = 100
a: Int = 100

scala> val b = 0X123Abc
b: Int = 1194684

scala> val c: Byte = 200
<console>:11: error: type mismatch;
 found   : Int(200)
 required: Byte
       val c: Byte = 200
                     ^

scala> val d = 200L
d: Long = 200

  Ⅱ、浮点数字面量 

浮点数的字面量都是十进制的,类型默认是Double类型。可以增加一个字母“e”或者“E”,再添加一个整数作为指数,这样就构成10的n次幂。最末尾可以写一个“f”或者“F”,表示Float类型;也可以写一个“d”或者“D”,表示Double类型。注意,Double类型的字面量不能赋给Float类型的变量。虽然Float允许扩展成Double类型,但是会发生精度损失。例如:

scala> val a = 1.2E3
a: Double = 1200.0

scala> val b = -3.2f
b: Float = -3.2

scala> val c: Float = -3.2
<console>:11: error: type mismatch;
 found   : Double(-3.2)
 required: Float
       val c: Float = -3.2
                      ^

scala> val d: Double = -3.2F
d: Double = -3.200000047683716

  Ⅲ、字符与字符串字面量

 字符字面量是以单引号' '包起来的一个字符,采用Unicode编码。也可以用'\u编码号'的方式来构造一个字符,而且Unicode编码可以出现在代码的任何地方,甚至是名称命名。此外,还支持转义字符。例如:

scala> val a = 'A'
a: Char = A

scala> val b = '\u0041'
b: Char = A

scala> val c = '\u0042'
c: Char = B

scala> val \u0041\u0042 = 1
AB: Int = 1

scala> val d = '\\'
d: Char = \

字符串就是用双引号" "包起来的字符序列,长度任意,允许掺杂转义字符。此外,也可以用前后各三个双引号"""  """包起来,这样字符串里也能出现双引号,而且转义字符不会被解读。例如:

scala> val a = "\\\\\\"
a: String = \\\

scala> val b = """So long \u0041 String \\\'\"!"""
b: String = So long A String \\\'\"!

  Ⅳ、字符串插值

Scala包括了一个灵活的机制来支持字符串插值,这使得表达式可以被嵌入在字符串字面量中并被求值。第一种形式是s插值器,即在字符串的双引号前加一个s,形如s“…${表达式}…”。s插值器会对内嵌的每个表达式求值,对求值结果调用内置的toString方法,替换掉字面量中的表达式。从美元符号开始到首个非标识符字符(字母、数字、下划线和操作符的组合称为标识符,以及反引号对` `包起来的字符串)的部分会被当作表达式,如果有非标识符字符,就必须放在花括号里,且左花括号要紧跟美元符号。第二种形式是raw插值器,它与s插值器类似,只不过不识别转义字符。第三种形式是f插值器,允许给内嵌的表达式加上printf风格的指令,指令放在表达式之后并以百分号开始。指令语法来自java.util.Formatter。如:

scala> val name = "ABC"
name: String = ABC

scala> println(s"$name DEFG")
ABC DEFG

scala> s"Sum = ${1 + 10}"
res0: String = Sum = 11

scala> s"\\\\"
res1: String = \\

scala> raw"\\\\"
res2: String = \\\\

scala> printf(f"${math.Pi}%.5f")
3.14159

三、总结

本章介绍了Scala定义变量的方法及基本变量类型,重点在于学会使用val类型的变量。

 

上一章   Scala入门——让你的代码跑起来

下一章   Scala基础——函数及其几种形式

  • 31
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值