十六、Scala
详细Scala教程见:Scala 教程 | 菜鸟教程 (runoob.com)
16.1 Scala基础
16.1.1 标识符
-
区分大小写
-
类名
- 类名的第一个字母要大写
- 几个单词来构成一个类的名称,每个单词的第一个字母要大写
-
方法名称
- 方法名称的第一个字母用小写
- 若干单词被用于构成方法的名称,则每个单词的第一个字母应大写
-
关键字
abstract case catch class def do else extends false final finally for forSome if implicit import lazy match new null object override package private protected return sealed super this throw trait try true type val var while with yield - : = => <- <: <% >: # @
16.1.2 数据类型
-
理解
- Scala的数据类型都是对象,也就是说scala没有java中的原生类型。在scala是可以对数字等基础类型调用方法的
-
数据类型
数据类型 描述 Byte 8位有符号补码整数。数值区间为 -128 到 127 Short 16位有符号补码整数。数值区间为 -32768 到 32767 Int 32位有符号补码整数。数值区间为 -2147483648 到 2147483647 Long 64位有符号补码整数。数值区间为 -9223372036854775808 到 9223372036854775807 Float 32 位, IEEE 754 标准的单精度浮点数 Double 64 位 IEEE 754 标准的双精度浮点数 Char 16位无符号Unicode字符, 区间值为 U+0000 到 U+FFFF String 字符序列 Boolean true或false Unit 表示无值,和其他语言中void等同。用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()。 Null null 或空引用 Nothing Nothing类型在Scala的类层级的最底端;它是任何其他类型的子类型。 Any Any是所有其他类的超类 AnyRef AnyRef类是Scala里所有引用类(reference class)的基类 -
数据类型关系图解
- 注意
16.1.3 变量和常量
- 变量声明
-
变量
-
在程序运行过程中其值可能发生改变的量叫做变量,如时间
-
用
var
来声明-
var myVar : String = "Foo"
-
以上定义了变量 myVar,我们可以修改它
-
-
-
常量
-
在程序运行过程中其值不会发生变化的量叫做常量,如 3
-
用
val
来声明-
val myVal : String = "Foo"
-
以上定义了常量 myVal,它是不能修改的。如果程序尝试修改常量 myVal 的值,程序将会在编译时报错
-
-
-
变量类型声明
-
变量的类型在变量名之后等号之前声明。定义变量的类型的语法格式如下:
var VariableName : DataType [= Initial Value] 或 val VariableName : DataType [= Initial Value]
-
-
变量类型引用
-
在 Scala 中声明变量和常量不一定要指明数据类型,在没有指明数据类型的情况下,其数据类型是通过变量或常量的初始值推断出来的。
-
所以,如果在没有指明数据类型的情况下声明变量或常量必须要给出其初始值,否则将会报错
var myVar = 10; val myVal = "Hello, Scala!";
以上实例中,myVar 会被推断为 Int 类型,myVal 会被推断为 String 类型
-
-
Scala多个变量声明
-
Scala 支持多个变量的声明:
val xmax, ymax = 100 // xmax, ymax都声明为100
-
如果方法返回值是元组,我们可以使用 val 来声明一个元组:
scala> val pa = (40,"Foo") pa: (Int, String) = (40,Foo)
-
16.1.4 运算符
定义: 一个运算符是一个符号,用于告诉编译器来执行指定的数学运算和逻辑运算
1. 算术运算符
假定变量 A 为 10,B 为 20:
运算符 | 描述 | 实例 |
---|---|---|
+ | 加号 | A + B 运算结果为 30 |
- | 减号 | A - B 运算结果为 -10 |
* | 乘号 | A * B 运算结果为 200 |
/ | 除号 | B / A 运算结果为 2 |
% | 取余 | B % A 运算结果为 0 |
实例
object Test {
def main(args: Array[String]) {
var a = 10;
var b = 20;
var c = 25;
var d = 25;
println("a + b = " + (a + b) );
println("a - b = " + (a - b) );
println("a * b = " + (a * b) );
println("b / a = " + (b / a) );
println("b % a = " + (b % a) );
println("c % a = " + (c % a) );
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
a + b = 30
a - b = -10
a * b = 200
b / a = 2
b % a = 0
c % a = 5
2. 关系运算符
假定变量 A 为 10,B 为 20:
运算符 | 描述 | 实例 |
---|---|---|
== | 等于 | (A == B) 运算结果为 false |
!= | 不等于 | (A != B) 运算结果为 true |
> | 大于 | (A > B) 运算结果为 false |
< | 小于 | (A < B) 运算结果为 true |
>= | 大于等于 | (A >= B) 运算结果为 false |
<= | 小于等于 | (A <= B) 运算结果为 true |
示例
object Test {
def main(args: Array[String]) {
var a = 10;
var b = 20;
println("a == b = " + (a == b) );
println("a != b = " + (a != b) );
println("a > b = " + (a > b) );
println("a < b = " + (a < b) );
println("b >= a = " + (b >= a) );
println("b <= a = " + (b <= a) );
}
}
输出结果
$ scalac Test.scala
$ scala Test
a == b = false
a != b = true
a > b = false
a < b = true
b >= a = true
b <= a = false
3. 逻辑运算符
假定变量 A 为 1,B 为 0:
运算符 | 描述 | 实例 |
---|---|---|
&& | 逻辑与,一假为假,同真为真 | (A && B) 运算结果为 false, |
|| | 逻辑或,一真则真,同假为假 | (A || B) 运算结果为 true |
! | 逻辑非,取反 | !(A && B) 运算结果为 true |
示例
object Test {
def main(args: Array[String]) {
var a = true;
var b = false;
println("a && b = " + (a&&b) );
println("a || b = " + (a||b) );
println("!(a && b) = " + !(a && b) );
}
}
//输出结果
$ scalac Test.scala
$ scala Test
a && b = false
a || b = true
!(a && b) = true
4. 位运算符
位运算符用来对二进制位进行操作,~,&,|,^ 分别为取反,按位与,按位或,按位异或运算,如下表实例:
p | q | p & q | p | q | p ^ q |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 1 | 0 | 1 | 1 |
1 | 1 | 1 | 1 | 0 |
1 | 0 | 0 | 1 | 1 |
如果指定 A = 60; 及 B = 13; 两个变量对应的二进制为:
A = 0011 1100
B = 0000 1101
-------位运算----------
A&B = 0000 1100
A|B = 0011 1101
A^B = 0011 0001
~A = 1100 0011
Scala 中的按位运算法则如下:
运算符 | 描述 | 实例 |
---|---|---|
& | 按位与运算符 | (a & b) 输出结果 12 ,二进制解释: 0000 1100 |
| | 按位或运算符 | (a | b) 输出结果 61 ,二进制解释: 0011 1101 |
^ | 按位异或运算符 | (a ^ b) 输出结果 49 ,二进制解释: 0011 0001 |
~ | 按位取反运算符 | (~a ) 输出结果 -61 ,二进制解释: 1100 0011, 在一个有符号二进制数的补码形式。 |
<< | 左移动运算符 | a << 2 输出结果 240 ,二进制解释: 1111 0000 |
>> | 右移动运算符 | a >> 2 输出结果 15 ,二进制解释: 0000 1111 |
>>> | 无符号右移 | A >>>2 输出结果 15, 二进制解释: 0000 1111 |
示例
object Test {
def main(args: Array[String]) {
var a = 60; /* 60 = 0011 1100 */
var b = 13; /* 13 = 0000 1101 */
var c = 0;
c = a & b; /* 12 = 0000 1100 */
println("a & b = " + c );
c = a | b; /* 61 = 0011 1101 */
println("a | b = " + c );
c = a ^ b; /* 49 = 0011 0001 */
println("a ^ b = " + c );
c = ~a; /* -61 = 1100 0011 */
println("~a = " + c );
c = a << 2; /* 240 = 1111 0000 */
println("a << 2 = " + c );
c = a >> 2; /* 15 = 1111 */
println("a >> 2 = " + c );
c = a >>> 2; /* 15 = 0000 1111 */
println("a >>> 2 = " + c );
}
}
//输出结果
$ scalac Test.scala
$ scala Test
a & b = 12
a | b = 61
a ^ b = 49
~a = -61
a << 2 = 240
a >> 2 = 15
a >>> 2 = 15
5. 赋值运算符
运算符 | 描述 | 实例 |
---|---|---|
= | 简单的赋值运算,指定右边操作数赋值给左边的操作数。 | C = A + B 将 A + B 的运算结果赋值给 C |
+= | 相加后再赋值,将左右两边的操作数相加后再赋值给左边的操作数。 | C += A 相当于 C = C + A |
-= | 相减后再赋值,将左右两边的操作数相减后再赋值给左边的操作数。 | C -= A 相当于 C = C - A |
*= | 相乘后再赋值,将左右两边的操作数相乘后再赋值给左边的操作数。 | C *= A 相当于 C = C * A |
/= | 相除后再赋值,将左右两边的操作数相除后再赋值给左边的操作数。 | C /= A 相当于 C = C / A |
%= | 求余后再赋值,将左右两边的操作数求余后再赋值给左边的操作数。 | C %= A is equivalent to C = C % A |
<<= | 按位左移后再赋值 | C <<= 2 相当于 C = C << 2 |
>>= | 按位右移后再赋值 | C >>= 2 相当于 C = C >> 2 |
&= | 按位与运算后赋值 | C &= 2 相当于 C = C & 2 |
^= | 按位异或运算符后再赋值 | C ^= 2 相当于 C = C ^ 2 |
|= | 按位或运算后再赋值 | C |= 2 相当于 C = C | 2 |
示例
object Test {
def main(args: Array[String]) {
var a = 10;
var b = 20;
var c = 0;
c = a + b;
println("c = a + b = " + c );
c += a ;
println("c += a = " + c );
c -= a ;
println("c -= a = " + c );
c *= a ;
println("c *= a = " + c );
a = 10;
c = 15;
c /= a ;
println("c /= a = " + c );
a = 10;
c = 15;
c %= a ;
println("c %= a = " + c );
c <<= 2 ;
println("c <<= 2 = " + c );
c >>= 2 ;
println("c >>= 2 = " + c );
c >>= a ;
println("c >>= a = " + c );
c &= a ;
println("c &= 2 = " + c );
c ^= a ;
println("c ^= a = " + c );
c |= a ;
println("c |= a = " + c );
}
}
//输出结果
$ scalac Test.scala
$ scala Test
c = a + b = 30
c += a = 40
c -= a = 30
c *= a = 300
c /= a = 1
c %= a = 5
c <<= 2 = 20
c >>= 2 = 5
c >>= a = 0
c &= 2 = 0
c ^= a = 10
c |= a = 10
16.1.5 逻辑语句
Scala IF…ELSE 语句是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块。
/**
* StdIn键盘标准输入 scala.io包下的
*/
def main(args: Array[String]): Unit = {
println("请输入年龄:")
val age = StdIn.readInt()
if(age >= 18 && age < 100){
println("成年")
}else if( age > 0 && age < 18){
println("未成年")
}else{
println("???")
}
}
16.1.6 循环语句
-
区间的获取
- to和until
- 1 to 10 返回1到10的Range数组,包含10
- 1 until 10 返回1到10 Range数组 ,不包含10
(1 to 10).foreach(print)//打印 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 (1 to 10).foreach(print)//与上面等价,打印 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 println(1 to (10 ,2))//步长为2,从1开始打印 ,1,3,5,7,9 println(1.to(10, 2)) println(1 until 10 ) //不包含最后一个数,打印 1,2,3,4,5,6,7,8,9 println(1.until(10))//与上面等价 println(1 until (10 ,3 ))//步长为2,从1开始打印,打印1,4,7
-
普通for循环
-
Range 可以是一个数字区间表示
i to j
,或者i until j
。左箭头<-
用于为变量 x 赋值 -
语法格式示例
for( var x <- Range ){ statement(s); }
-
-
多层for循环
-
可以分号隔开,写入多个list赋值的变量,构成多层for循环
-
语法格式示例
//scala中 不能写count++ count-- 只能写count+ var count = 0; for(i <- 1 to 10; j <- 1 until 10){ println("i="+ i +", j="+j) count += 1 } println(count); //例子: 打印小九九 for(i <- 1 until 10 ;j <- 1 until 10){ if(i>=j){ print(i +" * " + j + " = "+ i*j+" ") } if(i==j ){ println() } }
-
-
判断for循环
-
可以在for循环中加入条件判断
for (i<- 1 to 10; if (i%3)==0; if (i==4)){ println(i) }
-
-
for循环与yield
-
yield 关键字返回一个集合
for {子句} yield {变量或表达式}
,原来的集合不会被改变,只会通过你的for/yield构建出一个新的集合 -
将 for 循环的返回值作为一个变量存储
var retVal = for{ var x <- List if condition1; if condition2... }yield x
-
示例
object Test { def main(args: Array[String]) { var a = 0; val numList = List(1,2,3,4,5,6,7,8,9,10); // for 循环 var retVal = for{ a <- numList if a != 3; if a < 8 }yield a // 输出返回值 for( a <- retVal){ println( "Value of a: " + a ); } } } //输出结果 Value of a: 1 Value of a: 2 Value of a: 4 Value of a: 5 Value of a: 6 Value of a: 7
-
-
while循环(两种方式),
while(){}
,do {}while()
//将for中的符合条件的元素通过yield关键字返回成一个集合 val list = for(i <- 1 to 10 ; if(i > 5 )) yield i for( w <- list ){ println(w) } /** * while 循环 */ var index = 0 while(index < 100 ){ println("第"+index+"次while 循环") index += 1 } index = 0 do{ index +=1 println("第"+index+"次do while 循环") }while(index <100 )
16.2 方法与函数
16.2.1 方法的定义
-
方法的声明
- 用
def
语句定义方法
def functionName ([参数列表]) : [return type]
- 注意
- 如果你不写等于号和方法主体,那么方法会被隐式声明为抽象(abstract),包含它的类型于是也是一个抽象类型。
- 用
-
方法的定义
- 方法定义由一个 def 关键字开始,紧接着是可选的参数列表,一个冒号 : 和方法的返回类型,一个等于号 = ,最后是方法的主体
- 语法格式
def functionName ([参数列表]) : [return type] = { function body return [expr] }
-
方法调用
- 调用方法标准格式
functionName( 参数列表 )
- 如果方法使用了实例的对象来调用,我们可以使用类似java的格式 (使用 . 号):
[instance.]functionName( 参数列表 )
-
注意
- 在参数后面加一个冒号和类型来显式地指定返回类型
- 方法可以写返回值的类型也可以不写,会自动推断,但是如果有显式的Return有时候不能省略,必须写
- Scala 中函数有返回值时,可以写 return ,也可以不写 return ,不写 return 时会把函数中最后一行当做结果返回
- 如果去掉方法体前面的等号,那么这个方法返回类型必定是 Unit 的。scala可以把任意类型转换为 Unit
- 假设,函数里面的逻辑最后返回了一个 string ,那么这个返回值会被转换成 Unit ,原本逻辑的值会被丢弃
- 如果返回值可以一行搞定,可以将 {} 省略不写
- scala 规定方法的传过来的参数为 val 的,不是 var 的
def functionName ([参数列表]) : [return type] = { function body return [expr] } def method1 (a: Int , b: Int ) : Unit = { println(a+b) } method1(1,1) def method2 (a : Int , b : Int) = a+b println(method2(1,2)) def method3(s: String) { return s } println(method3("m3"))
16.2.2 函数的定义
-
理解
- Scala的函数是基于Function家族,0-22,一共23个Function Trait可以被使用,数字代表了Funtcion的入参个数
-
示例
var f1 = (s: String) => { println("hi1 " + s); } var f2 = (s: String) => { println("hi2 " + s); }
16.2.3 方法与函数的关系
-
理解关系
- Scala 方法是类的一部分,而函数是一个对象,对象的引用可以赋值给一个变量。换句话来说在类中定义的函数即是方法
- Scala 中的方法跟 Java 的类似,方法是组成类的一部分。
- Scala 中的函数则是一个完整的对象,Scala 中的函数其实就是继承了 Trait 的类的对象
-
两者的区别
-
定义语句的区别
- Scala 中使用 val 语句可以定义函数,def 语句定义方法
def m(x: Int) = x + 3 val f = (x: Int) => x + 3
-
当函数定义在类中,就表示方法,其他的都称为函数
-
函数可以作为一个参数传入到方法中,而方法就不行
- 在Scala中无法直接操作方法,如果要操作方法,必须先将其转换成函数,
- 通常情况下,编译器会自动将方法转换成函数
val f = m _
//表示将 m 方法转换为函数
def msum(nums: Array[Int]) = { var sum = 0; for (num <- nums) { sum += num; } sum } var fsum = msum _ println(fsum(Array[Int](1, 2, 3, 4, 5)));
- 在需要函数的地方,如果传递一个方法,会自动进行ETA展开(把方法转换为函数)
def main(args: Array[String]): Unit = { method02(method01, "libai") } def method01(s: String) = { println("hi4 " + s); } def method02(fun: String => Unit, str: String) = { fun(str); }
-
函数必须要有参数列表,而方法可以没有参数列表
-
如果我们直接把一个方法赋值给变量会报错,(Missing arguments for methodmethod01(Int,Int))
-