Scala学习笔记 java语言的脚本化。
【菜鸟教程】https://www.runoob.com/scala/scala-tutorial.html
一、安装
下载 https://www.scala-lang.org/download/
安装【推荐下载2.11.12,后面spark2.4.3匹配这个版本】
配置环境变量(菜鸟上有教程)
CMD中运行
>scala Welcome to Scala 2.13.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_251). Type in expressions for evaluation. Or try :help. scala> println("Hello World!") Hello World!
注意 JDK版本,可能会报错
>java -version java version "1.8.0_251"
二、IDEA中的Scala插件
安装插件
创建项目/模块
创建一个对象
编写代码
object Hello { def main (args: Array [String ]): Unit = { print("Hello World!" ) } }
运行
三、变量 3.1 分类
scala> var a = 1 a: Int = 1 scala> val b = 2 b: Int = 2 scala> b = 5 ^ error: reassignment to val scala> val str:String = "kkk" str: String = kkk
3.2 类型
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)的基类
上表中列出的数据类型都是对象,也就是说scala没有java中的原生类型。在scala是可以对数字等基础类型调用方法的。
3.3 类型转换 scala> var a = 111 a: Int = 111 scala> var b = a.toString b: String = 111
自定义类型检查和转换
$scala>class Animal{} $scala>class Dog extends Animal{} $scala>val d = new Dog(); $scala>d.isInstanceOf[Animal] //true,===> instanceOf $scala>val a = d.asInstanceOf[Animal] //强转,===> (Animal)d //得带对象的类 $scala>d.getClass //d.getClass(); $scala>d.getClass == classOf[Dog] //精确匹配
3.4 lazy 延迟计算 有时候可以按需加载,即需要的时候再进行计算。
scala> lazy val x = scala.io.Source.fromFile("d:/tmp") x: scala.io.BufferedSource = <lazy> // 当我们使用x的时候,才会进行计算 scala> x java.io.FileNotFoundException: d:\tmp (拒绝访问。) at java.io.FileInputStream.open0(Native Method) at java.io.FileInputStream.open(FileInputStream.java:195) at java.io.FileInputStream.<init>(FileInputStream.java:138) at scala.io.Source$.fromFile(Source.scala:92) at scala.io.Source$.fromFile(Source.scala:77) at scala.io.Source$.fromFile(Source.scala:55) at .x$lzycompute(<console>:1) at .x(<console>:1) ... 28 elided
3.5 表达式 任何一条表达式都有返回值。如果一个{}中,有多个表达式,其值为最后一个表达式的值。
如果只有一个表达式可以不加分号(;),多个就要用分号隔开。
四、条件判断 4.1 if-else scala> if (1 > 2) { | print("if-1") | } else if (1 == 2) { | print("if-2") | } else print("if-3") if-3 scala>
注意:如果要换行,要用{}包起来,不然会报错!
五、循环语句 5.1 while scala> var a = 1 a: Int = 1 scala> while(a < 5) {println(a); a += 1;} 1 2 3 4
还有 do … while
5.2 for 闭区间
scala> for (i <- 1 to 5) println(i) 1 2 3 4 5
左闭右开区间
scala> for (i <- 1 until 5) println(i) 1 2 3 4
5.3 循环中断 scala中没有break和continue来中止循环。但是提供了一个break 语句来实现。
scala> var a = 1 a: Int = 1 scala> while(true){ | println(a); | a += 1; | if(a >= 3) break(); | } 1 2 scala.util.control.BreakControl scala>
5.4 for 循环过滤 可以在for中对不满足条件的进行过滤操作。
打印出偶数:
scala> for (i <- 1 to 10 if i % 2 == 0) println(i) 2 4 6 8 10
5.4 for 双重循环 scala> for (i <- 1 to 3; j <- 1 to 2) { | printf("i: %d, j: %d\n", i, j); | } i: 1, j: 1 i: 1, j: 2 i: 2, j: 1 i: 2, j: 2 i: 3, j: 1 i: 3, j: 2
5.5 for 使用 yield 你可以将 for 循环的返回值作为一个变量存储。语法格式如下:
scala> for (x <- 1 to 10) yield x res18: IndexedSeq[Int] = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
循环中的 yield 会把当前的元素记下来,保存在集合中,循环结束后将返回该集合。
六、访问修饰符 Scala 访问修饰符基本和Java的一样,分别有:private ,protected ,public 。
如果没有指定访问修饰符,默认情况下,Scala 对象的访问级别都是 public。
Scala 中的 private 限定符,比 Java 更严格,在嵌套类情况下,外层类甚至不能访问被嵌套类的私有成员。
作用域保护
Scala中,访问修饰符可以通过使用限定词强调。格式为:
private[x] 或 protected[x]
这里的x指代某个所属的包、类或单例对象。如果写成private[x],读作”这个成员除了对[…]中的类或[…]中的包中的类及它们的伴生对像可见外,对其它所有类都是private。
这种技巧在横跨了若干包的大型项目中非常有用,它允许你定义一些在你项目的若干子包中可见但对于项目外部的客户却始终不可见的东西。
七、方法与函数 Scala 有方法与函数,二者在语义上的区别很小。Scala 方法是类的一部分,而函数是一个对象可以赋值给一个变量。换句话来说在类中定义的函数即是方法。
Scala 中的方法跟 Java 的类似,方法是组成类的一部分。
Scala 中的函数则是一个完整的对象,Scala 中的函数其实就是继承了 Trait 的类的对象。
Scala 中使用 val 语句可以定义函数 ,def 语句定义方法 。
class Test{ def m(x: Int) = x + 3 val f = (x: Int) => x + 3 }
7.1 方法定义 方法定义由一个 def 关键字开始,紧接着是可选的参数列表,一个冒号 : 和方法的返回类型,一个等于号 = ,最后是方法的主体。
Scala 方法定义格式如下:
def functionName ([参数列表]) : [return type] = { function body return [expr] }
例子:
def addInt( a:Int, b:Int ) : Int = { var sum:Int = 0 sum = a + b return sum }
如果返回类型是空(即JAVA中的void),那么返回类型Unit 可以省略;如果参数为空,那么() 可以省略:
def printMe( ) : Unit = { println("Hello, Scala!") }
等价于:
scala> def printMe = println("Hello, Scala!") printMe: Unit scala> printMe Hello, Scala!
7.2 递归函数 递归函数必须显式定义返回类型!
打印阶乘:
scala> def fac(n:Int):Int = if(n == 1) 1 else n * fac(n - 1); fac: (n: Int)Int scala> fac(3) res24: Int = 6
7.3 函数参数默认值 以下是定义了3个参数,其中1.3位带默认参数。的各种情况。
scala> def decorate(prefix:String = "[[",str:String,suffix:String = "]]") = { | prefix + str + suffix | } decorate: (prefix: String, str: String, suffix: String)String // 可以指定参数名赋值 scala> decorate(str="hello") res26: String = [[hello]] // 参数不够 scala> decorate("11") ^ error: not enough arguments for method decorate: (prefix: String, str: String, suffix: String)String. Unspecified value parameter str. // 部分参数 scala> decorate("11","22") res30: String = 1122]] // 全部参数 scala> decorate("<<","22",">>") res32: String = <<22>> // 指定参数名,但不按定义顺序 scala> decorate(str="hello",prefix="<<") res34: String = <<hello]]
7.4 可变参数 可变参数带个星号(*):
scala> def sum(a:Int*) = { | var s = 0; | for (x <- a) s += x; | s | } sum: (a: Int*)Int // 错误!scala.collection.immutable.Range.Inclusive = Range 1 to 10 scala> sum(1 to 10) ^ error: type mismatch; found : scala.collection.immutable.Range.Inclusive required: Int // 正确!将1 to 10当做序列处理 scala> sum(1 to 10:_*) res36: Int = 55
7.5 apply() 和 update() Array(100) //Array.apply(100);
Array(100) = 200 //Array.update(200)7.6 闭包 可以简单的理解为:一个函数中,使用了函数外部的一个变量。
var factor = 3 val multiplier = (i:Int) => i * factor
七、异常 Scala 的异常处理和其它语言比如 Java 类似。
Scala 的方法可以通过抛出异常的方法的方式来终止相关代码的运行,不必通过返回值。
7.1 抛出异常 Scala 抛出异常的方法和 Java一样,使用 throw 方法,例如,抛出一个新的参数异常:
throw new IllegalArgumentException
7.2 try | catch | finally 异常捕捉的机制与其他语言中一样,如果有异常发生,catch字句是按次序捕捉的。因此,在catch字句中,越具体的异常越要靠前,越普遍的异常越靠后。 如果抛出的异常不在catch字句中,该异常则无法处理,会被升级到调用者处。
捕捉异常的catch子句,语法与其他语言中不太一样。在Scala里,借用了模式匹配的思想来做异常的匹配,因此,在catch的代码里,是一系列case 字句。
finally 语句用于执行不管是正常处理还是有异常发生时都需要执行的步骤。
例子:
import java.io.FileReader import java.io.FileNotFoundException import java.io.IOException object Test { def main (args: Array [String ]) { try { val f = new FileReader ("input.txt" ) } catch { case ex: FileNotFoundException => { println("Missing file exception" ) } case ex: IOException => { println("IO Exception" ) } } finally { println("Exiting finally..." ) } } }
执行以上代码,输出结果为:
$ scalac Test.scala $ scala Test Missing file exception Exiting finally...
八、字符串 在 Scala 中,字符串的类型实际上是 Java String,它本身没有 String 类。
在 Scala 中,String 是一个不可变的对象,所以该对象不可被修改。这就意味着你如果修改字符串就会产生一个新的字符串对象。
8.1 创建字符串 不可变
scala> var str = "Hello World!" str: String = Hello World!
scala> var str:String = "Hello World!" str: String = Hello World!
可变
我们前面提到过 String 对象是不可变的,如果你需要创建一个可以修改的字符串,可以使用 String Builder 类,如下实例:
object Test { def main(args: Array[String]) { val buf = new StringBuilder; buf += 'a' buf ++= "bcdef" println( "buf is : " + buf.toString ); } }
执行以上代码,输出结果为:
$ scalac Test.scala $ scala Test buf is : abcdef
8.2 字符串长度 scala> str.length() res43: Int = 12
8.3 字符串连接 scala> str.concat("I'm Tom.") res45: String = Hello World!I'm Tom.
scala> "Hello," + "Scala" res46: String = Hello,Scala
8.4 String 其他方法 可以像使用JAVA中的String方法到Scala中来使用!
【菜鸟示例】https://www.runoob.com/scala/scala-strings.html
八、数组 8.1 定长数组 可以理解[]相当于JAVA中<>的泛型:
var z = new Array[String](3)
以上语法中,z 声明一个字符串类型的数组,数组长度为 3 ,可存储 3 个元素。我们可以为每个元素设置值,并通过索引来访问每个元素,如下所示:
scala> var z = new Array[String](3) z: Array[String] = Array(null, null, null) // 根据下表赋值 scala> z(0) = "hello" scala> z res48: Array[String] = Array(hello, null, null) // 根据下表获取数据 scala> z(0) res49: String = hello
注意:
scala> var z = Array(1,2,3) z: Array[Int] = Array(1, 2, 3)
这里同样可以创建数组!但是这里代表的意思是:创建一个数组,并赋予3个初始值! 而上面那个是创建一个长度为3的数组 !
8.2 变长数组 类似C++中的Vector。
创建数组缓冲区对象
scala> import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.ArrayBuffer scala> val bufArray = ArrayBuffer[Int]() bufArray: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer()
末尾追加一个元素
scala> bufArray += 1 res53: bufArray.type = ArrayBuffer(1)
末尾追加一个数组
scala> bufArray ++= Array(2,3,4) res55: bufArray.type = ArrayBuffer(1, 2, 3, 4)
删除末尾N个元素
scala> bufArray.trimEnd(2) scala> bufArray res59: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(1, 2)
在索引处插入元素
scala> bufArray.insert(0,0) scala> bufArray res61: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(0, 1, 2)
删除索引处元素
scala> bufArray.remove(1) res62: Int = 1 scala> bufArray res63: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(0, 2)
删除索引处开始N个元素
scala> bufArray.remove(0, 2) scala> bufArray res66: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer()
转为普通数组
8.3 多维数组 多维数组一个数组中的值可以是另一个数组,另一个数组的值也可以是一个数组。矩阵与表格是我们常见的二维数组。
创建矩阵数组
scala> var a = Array.ofDim[Int](3,3) a: Array[Array[Int]] = Array(Array(0, 0, 0), Array(0, 0, 0), Array(0, 0, 0))
8.4 数组常用方法 排序
scala> var a = Array(1,5,3,2,4) a: Array[Int] = Array(1, 5, 3, 2, 4) scala> import scala.util.Sorting._ import scala.util.Sorting._ scala> quickSort(a) scala> a res71: Array[Int] = Array(1, 2, 3, 4, 5)
求和
scala> a.sum res67: Int = 15
最大(小)值
scala> a.min res68: Int = 1 scala> a.max res69: Int = 5
格式输出
以什么字符串开头,什么作为分隔符,什么作为结束。
scala> a.mkString("[",",","]") res72: String = [1,2,3,4,5]
数组合并
scala> var b = Array(6,7,8) b: Array[Int] = Array(6, 7, 8) scala> a ++ b res76: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8) 或 scala> a.concat(b) res77: Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8)
创建区间数组
第三个参数是步长
scala> var c = Array.range(1, 10, 2) c: Array[Int] = Array(1, 3, 5, 7, 9)
Scala数组所有方法
下表中为 Scala 语言中处理数组的重要方法,使用它前我们需要使用 import Array._ 引入包。
序号
方法和描述
1
def apply( x: T, xs: T* ): Array[T] 创建指定对象 T 的数组, T 的值可以是 Unit, Double, Float, Long, Int, Char, Short, Byte, Boolean。
2
def concat[T]( xss: Array[T]* ): Array[T] 合并数组
3
def copy( src: AnyRef, srcPos: Int, dest: AnyRef, destPos: Int, length: Int ): Unit 复制一个数组到另一个数组上。相等于 Java’s System.arraycopy(src, srcPos, dest, destPos, length)。
4
def empty[T]: Array[T] 返回长度为 0 的数组
5
def iterate[T]( start: T, len: Int )( f: (T) => T ): Array[T] 返回指定长度数组,每个数组元素为指定函数的返回值。以上实例数组初始值为 0,长度为 3,计算函数为a=>a+1 :scala> Array.iterate(0,3)(a=>a+1) res1: Array[Int] = Array(0, 1, 2)
6
def fill[T]( n: Int )(elem: => T): Array[T] 返回数组,长度为第一个参数指定,同时每个元素使用第二个参数进行填充。
7
def fill[T]( n1: Int, n2: Int )( elem: => T ): Array[Array[T]] 返回二数组,长度为第一个参数指定,同时每个元素使用第二个参数进行填充。
8
def ofDim[T]( n1: Int ): Array[T] 创建指定长度的数组
9
def ofDim[T]( n1: Int, n2: Int ): Array[Array[T]] 创建二维数组
10
def ofDim[T]( n1: Int, n2: Int, n3: Int ): Array[Array[Array[T]]] 创建三维数组
11
def range( start: Int, end: Int, step: Int ): Array[Int] 创建指定区间内的数组,step 为每个元素间的步长
12
def range( start: Int, end: Int ): Array[Int] 创建指定区间内的数组
13
def tabulate[T]( n: Int )(f: (Int)=> T): Array[T] 返回指定长度数组,每个数组元素为指定函数的返回值,默认从 0 开始。以上实例返回 3 个元素:scala> Array.tabulate(3)(a => a + 5) res0: Array[Int] = Array(5, 6, 7)
14
def tabulate[T]( n1: Int, n2: Int )( f: (Int, Int ) => T): Array[Array[T]] 返回指定长度的二维数组,每个数组元素为指定函数的返回值,默认从 0 开始。
九、集合 【菜鸟集合文档】https://www.runoob.com/scala/scala-collections.html
9.1 Map 映射 9.1.1 创建 可变 Map
// 空哈希表,键为字符串,值为整型 var A:Map[Char,Int] = Map() // Map 键值对演示 scala> var map = Map(1->"hello",2->"world") map: scala.collection.immutable.Map[Int,String] = Map(1 -> hello, 2 -> world)
不可变 Map
scala.collection.immutable.Map[Int,String]
9.1.2 添加与删除 添加
scala> map + (3->"Scala") res81: scala.collection.immutable.Map[Int,String] = Map(1 -> hello, 2 -> world, 3 -> Scala)
scala> map += (3->"Scala") scala> map res84: scala.collection.immutable.Map[Int,String] = Map(1 -> hello, 2 -> world, 3 -> Scala)
合并
注意!重复的Key会被后面覆盖
scala> var map1 = Map(1->"one",2->"two") map1: scala.collection.immutable.Map[Int,String] = Map(1 -> one, 2 -> two) scala> var map2 = Map(2->"towTmp",3->"three") map2: scala.collection.immutable.Map[Int,String] = Map(2 -> towTmp, 3 -> three) scala> map1 ++ map2 res93: scala.collection.immutable.Map[Int,String] = Map(1 -> one, 2 -> towTmp, 3 -> three)
删除
scala> map -= 1 scala> map res90: scala.collection.immutable.Map[Int,String] = Map(2 -> world, 3 -> Scala)
9.1.3 Key-Value 基本操作 获取元素
scala> map(2) res101: String = world
返回所有Key
scala> map.keys res85: Iterable[Int] = Set(1, 2, 3)
返回所有Value
scala> map.values res87: Iterable[String] = Iterable(hello, world, Scala)
判断空
scala> map.isEmpty res88: Boolean = false
查看Map中是否存在指定Key
scala> map.contains(3) res105: Boolean = true
9.1.4 遍历 scala> map1 res99: scala.collection.immutable.Map[Int,String] = Map(1 -> one, 2 -> two, 3 -> three) scala> map1.keys.foreach{i => printf("Key: %d, Value: %s\n", i, map1(i))} Key: 1, Value: one Key: 2, Value: two Key: 3, Value: three
scala> for((k, v) <- map1) printf("Key: %d, Value: %s\n", k, v) Key: 1, Value: one Key: 2, Value: two Key: 3, Value: three
9.2 Tuple 元组 元组:一组数据的集合
9.2.1 创建 以一个()返回
scala> var tuple = (1,"2",true) tuple: (Int, String, Boolean) = (1,2,true)
9.2.2 获取元素 scala> tuple._1 res109: Int = 1
注意:下标是1开始
9.2.3 zip 拉链操作 数组进行ZIP,两两组合成一个元组,多余无法匹配的被丢弃。
scala> var array1 = Array(1, 2, 3, 4) array1: Array[Int] = Array(1, 2, 3, 4) scala> var array2 = Array("One","Two","Three") array2: Array[String] = Array(One, Two, Three) scala> array1.zip(array2) res111: Array[(Int, String)] = Array((1,One), (2,Two), (3,Three))
9.3 List 列表 Scala 列表类似于数组,它们所有元素的类型都相同,但是它们也有所不同:列表是不可变的,值一旦被定义了就不能改变,其次列表 具有递归的结构(也就是链接表结构)而数组不是。。
9.3.1 创建 val site: List [String ] = List ("Runoob" , "Google" , "Baidu" )val nums: List [Int ] = List (1 , 2 , 3 , 4 )val empty: List [Nothing ] = List ()val dim: List [List [Int ]] = List ( List (1 , 0 , 0 ), List (0 , 1 , 0 ), List (0 , 0 , 1 ) )
构造列表的两个基本单位是 Nil 和 :: (:: 是右结合)
Nil 也可以表示为一个空列表。
以上实例我们可以写成如下所示:
val site = "Runoob" :: ("Google" :: ("Baidu" :: Nil ))val nums = 1 :: (2 :: (3 :: (4 :: Nil )))val empty = Nil val dim = (1 :: (0 :: (0 :: Nil ))) :: (0 :: (1 :: (0 :: Nil ))) :: (0 :: (0 :: (1 :: Nil ))) :: Nil
fill
我们可以使用 List.fill() 方法来创建一个指定重复数量的元素列表:
object Test { def main (args: Array [String ]) { val site = List .fill(3 )("Runoob" ) println( "site : " + site ) val num = List .fill(10 )(2 ) println( "num : " + num ) } }
9.3.2 基本操作
9.3.3 连接列表 你可以使用 ::: 运算符或 List.:::() 方法或 List.concat() 方法或ListA ++ ListB 来连接两个或多个列表。
object Test { def main (args: Array [String ]) { val site1 = "Runoob" :: ("Google" :: ("Baidu" :: Nil )) val site2 = "Facebook" :: ("Taobao" :: Nil ) var fruit = site1 ::: site2 println( "site1 ::: site2 : " + fruit ) fruit = site1.:::(site2) println( "site1.:::(site2) : " + fruit ) fruit = List .concat(site1, site2) println( "List.concat(site1, site2) : " + fruit ) } }
9.4 Set 集合 Scala Set(集合)是没有重复的对象集合,所有的元素都是唯一的。
Scala 集合分为可变的和不可变的集合。
默认情况下,Scala 使用的是不可变集合,如果你想使用可变集合,需要引用 scala.collection.mutable.Set 包。
9.4.1 创建、添加、删除 默认引用 scala.collection.immutable.Set,不可变集合实例如下:
val set = Set (1 ,2 ,3 )println(set.getClass.getName) println(set.exists(_ % 2 == 0 )) println(set.drop(1 ))
如果需要使用可变集合需要引入 scala.collection.mutable.Set:
import scala.collection.mutable.Set val mutableSet = Set (1 ,2 ,3 )println(mutableSet.getClass.getName) mutableSet.add(4 ) mutableSet.remove(1 ) mutableSet += 5 mutableSet -= 2 println(mutableSet) val another = mutableSet.toSetprintln(another.getClass.getName)
注意: 虽然可变Set和不可变Set都有添加或删除元素的操作,但是有一个非常大的差别。对不可变Set进行操作,会产生一个新的set,原来的set并没有改变,这与List一样。 而对可变Set进行操作,改变的是该Set本身,与ListBuffer类似。
9.4.2 连接集合 你可以使用 ++ 运算符或 Set.++() 方法来连接两个集合。如果元素有重复的就会移除重复的元素。实例如下:
object Test { def main (args: Array [String ]) { val site1 = Set ("Runoob" , "Google" , "Baidu" ) val site2 = Set ("Faceboook" , "Taobao" ) var site = site1 ++ site2 println( "site1 ++ site2 : " + site ) site = site1.++(site2) println( "site1.++(site2) : " + site ) } }
执行以上代码,输出结果为:
$ vim Test.scala $ scala Test.scala site1 ++ site2 : Set(Faceboook, Taobao, Google, Baidu, Runoob) site1.++(site2) : Set(Faceboook, Taobao, Google, Baidu, Runoob)
9.4.3 交集 你可以使用 Set.& 方法或 Set.intersect 方法来查看两个集合的交集元素。实例如下:
object Test { def main (args: Array [String ]) { val num1 = Set (5 ,6 ,9 ,20 ,30 ,45 ) val num2 = Set (50 ,60 ,9 ,20 ,35 ,55 ) println( "num1.&(num2) : " + num1.&(num2) ) println( "num1.intersect(num2) : " + num1.intersect(num2) ) } }
执行以上代码,输出结果为:
$ vim Test.scala $ scala Test.scala num1.&(num2) : Set(20, 9) num1.intersect(num2) : Set(20, 9)
十、迭代器 Scala Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法。
迭代器 it 的两个基本操作是 next 和 hasNext 。
调用 it.next() 会返回迭代器的下一个元素,并且更新迭代器的状态。
调用 it.hasNext() 用于检测集合中是否还有元素。
让迭代器 it 逐个返回所有元素最简单的方法是使用 while 循环:
object Test { def main(args: Array[String]) { val it = Iterator("Baidu", "Google", "Runoob", "Taobao") while (it.hasNext){ println(it.next()) } } }
执行以上代码,输出结果为:
$ scalac Test.scala $ scala Test Baidu Google Runoob Taobao
最大最小值
你可以使用 it.min 和 it.max 方法从迭代器中查找最大与最小元素,实例如下:
object Test { def main(args: Array[String]) { val ita = Iterator(20,40,2,50,69, 90) val itb = Iterator(20,40,2,50,69, 90) println("最大元素是:" + ita.max ) println("最小元素是:" + itb.min ) } }
执行以上代码,输出结果为:
$ scalac Test.scala $ scala Test 最大元素是:90 最小元素是:2
获取迭代器的长度
你可以使用 it.size 或 it.length 方法来查看迭代器中的元素个数。实例如下:
object Test { def main(args: Array[String]) { val ita = Iterator(20,40,2,50,69, 90) val itb = Iterator(20,40,2,50,69, 90) println("ita.size 的值: " + ita.size ) println("itb.length 的值: " + itb.length ) } }
执行以上代码,输出结果为:
$ scalac Test.scala $ scala Test ita.size 的值: 6 itb.length 的值: 6
【其他常用方法】https://www.runoob.com/scala/scala-iterators.html
十一、类 与 对象 类是对象的抽象,而对象是类的具体实例。类是抽象的,不占用内存,而对象是具体的,占用存储空间。类是用于创建对象的蓝图,它是一个定义包括在特定类型的对象中的方法和变量的软件模板。
如果不什声明修饰符,那么默认是public 。
11.1 定义类 示例
object Hello { class Point (val xc: Int ,var yc: Int ) { var x: Int = xc var y: Int = yc val name = "P1" private [this ] var id = 0 def move (dx: Int , dy: Int ) { x = x + dx y = y + dy println ("x 的坐标点: " + x) println ("y 的坐标点: " + y) } } def main (args: Array [String ]): Unit = { var p = new Point (1 , 2 ) println(p.x) p.x_=(5 ) println(p.x) p.x = 4 println(p.x) } }
运行结果
11.2 BeanProperty 注解 为了能更好的配合JAVA的一些工具类,提供了注解的方式,来生成JAVA风格的getter和setter。
import scala.beans.BeanProperty @BeanProperty var y: Int = yc
11.3 构造函数 11.3.1 主构造 class Person (val name:String ,var age:Int , id :Int ) { def hello () = println(id) }
编译 scalac test.scala 、 javap Person.class
public class Person { public java.lang.String name () ; public int age () ; public void age_$eq(int ); public void hello () ; public Person (java.lang.String, int , int ) ; }
可以看到如果在类参数中加上var\val或者不加,也会生成相应的属性。
11.3.2 辅助构造 注意: 使用辅助构造的时候,第一行必须有this()或this(…)
class Person { var id = 1 ; var name = "tom" ; var age = 12 ; def this (name:String ){ this (); this .name = name ; } def this (name:String ,age:Int ){ this (name) ; this .age = age ; } }
11.4 object 对象 11.4.1 单例对象 说明:
scala没有静态的概念,如果需要定义静态成员,可以通过object实现。object对象不能带参数。
编译完成后,会生成对应的类,方法都是静态方法,非静态成员对应到单例类中
单例类以Util$作为类名称。
object Util { private var brand = "benz" ; def hello () = println("hello world" ); }
def main (args: Array [String ]): Unit = { Util .hello() }
运行结果:
11.4.2 伴生对象 当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象:companion object。你必须在同一个源文件里定义类和它的伴生对象。类被称为是这个单例对象的伴生类:companion class。类和它的伴生对象可以互相访问其私有成员。
class Car { def stop () = { Car .run() println("stop...." ) } } object Car { private def run () = println("run..." ) } def main (args: Array [String ]): Unit = { new Car ().stop() }
输出结果
11.5 抽象类 与 继承 一个简单的例子
abstract class Animal (val name:String ) { val id:Int ; def run () ; }
Scala继承一个基类跟Java很相似, 但我们需要注意以下几点:
1、重写一个非抽象方法必须使用override修饰符。
2、只有主构造函数才可以往基类的构造函数里写参数。
3、在子类中重写超类的抽象方法时,你不需要使用override关键字。
接下来让我们来看个实例:
class Point (xc: Int , yc: Int ) { var x: Int = xc var y: Int = yc def move (dx: Int , dy: Int ) { x = x + dx y = y + dy println ("x 的坐标点: " + x); println ("y 的坐标点: " + y); } } class Location (override val xc: Int , override val yc: Int , val zc :Int ) extends Point (xc, yc ) { var z: Int = zc def move (dx: Int , dy: Int , dz: Int ) { x = x + dx y = y + dy z = z + dz println ("x 的坐标点 : " + x); println ("y 的坐标点 : " + y); println ("z 的坐标点 : " + z); } }
Scala 使用 extends 关键字来继承一个类。实例中 Location 类继承了 Point 类。Point 称为父类(基类),Location 称为子类。
override val xc 为重写了父类的字段。
继承会继承父类的所有属性和方法,Scala 只允许继承一个父类(单继承) 。
实例如下:
import java.io._class Point (val xc: Int , val yc: Int ) { var x: Int = xc var y: Int = yc def move (dx: Int , dy: Int ) { x = x + dx y = y + dy println ("x 的坐标点 : " + x); println ("y 的坐标点 : " + y); } } class Location (override val xc: Int , override val yc: Int , val zc :Int ) extends Point (xc, yc ) { var z: Int = zc def move (dx: Int , dy: Int , dz: Int ) { x = x + dx y = y + dy z = z + dz println ("x 的坐标点 : " + x); println ("y 的坐标点 : " + y); println ("z 的坐标点 : " + z); } } object Test { def main (args: Array [String ]) { val loc = new Location (10 , 20 , 15 ); loc.move(10 , 10 , 5 ); } }
执行以上代码,输出结果为:
$ scalac Test.scala $ scala Test x 的坐标点 : 20 y 的坐标点 : 30 z 的坐标点 : 20
Scala重写一个非抽象方法,必须用override修饰符。
class Person { var name = "" override def toString = getClass.getName + "[name=" + name + "]" } class Employee extends Person { var salary = 0.0 override def toString = super .toString + "[salary=" + salary + "]" } object Test extends App { val fred = new Employee fred.name = "Fred" fred.salary = 50000 println(fred) }
执行以上代码,输出结果为:
$ scalac Test.scala $ scala Test Employee[name=Fred][salary=50000.0]
十二、包 包对象,编译完之后生成以xxx为package,下面含有类package.class + package.class
package object xxxx {} a.a1.aa1.xxxx
限定符
导包
import java.io.Exception import java.io.{A,B,C} import java.io.{A => A0} //别名 import a._ 相当于Java的.*
十三、文件IO 13.1 写文件 Scala 进行文件写操作,直接用的都是 java中 的 I/O 类 (java.io.File ):
import java.io._object Test { def main (args: Array [String ]) { val writer = new PrintWriter (new File ("test.txt" )) writer.write("菜鸟教程" ) writer.close() } }
执行以上代码,会在你的当前目录下生产一个 test.txt 文件,文件内容为”菜鸟教程”:
$ scalac Test.scala $ scala Test $ cat test.txt 菜鸟教程
13.2 读文件 从文件读取内容非常简单。我们可以使用 Scala 的 Source 类及伴生对象来读取文件。以下实例演示了从 “test.txt”(之前已创建过) 文件中读取内容:
import scala.io.Source object Test { def main (args: Array [String ]) { println("文件内容为:" ) Source .fromFile("test.txt" ).foreach{ print } } }
执行以上代码,输出结果为:
$ scalac Test.scala $ scala Test 文件内容为: 菜鸟教程
13.3 从控制台读取 import scala.io.StdIn object IO { def main (args: Array [String ]): Unit = { val line = StdIn .readLine("请输入: " ) println("你输入的是: " + line) } }
十四、正则* 14.1 查找 14.2 替换 14.3 正则表达式 十五、Trait 特征 Scala Trait(特征) 相当于 Java 的接口,实际上它比接口还功能强大。
与接口不同的是,它还可以定义属性和方法的实现。
一般情况下Scala的类只能够继承单一父类,但是如果是 Trait(特征) 的话就可以继承多个,从结果来看就是实现了多重继承。
如果只有一个trait使用extends进行扩展,如果多个,使用with对剩余的trait进行扩展。
Trait(特征) 定义的方式与类类似,但它使用的关键字是 trait ,如下所示:
trait Equal { def isEqual (x: Any ): Boolean def isNotEqual (x: Any ): Boolean = !isEqual(x) }
以上Trait(特征)由两个方法组成:isEqual 和 isNotEqual 。isEqual 方法没有定义方法的实现,isNotEqual定义了方法的实现。子类继承特征可以实现未被实现的方法。所以其实 Scala Trait(特征)更像 Java 的抽象类。
以下演示了特征的完整实例:
trait Equal { def isEqual (x: Any ): Boolean def isNotEqual (x: Any ): Boolean = !isEqual(x) } class Point (xc: Int , yc: Int ) extends Equal { var x: Int = xc var y: Int = yc def isEqual (obj: Any ) = obj.isInstanceOf[Point ] && obj.asInstanceOf[Point ].x == x } object Test { def main (args: Array [String ]) { val p1 = new Point (2 , 3 ) val p2 = new Point (2 , 4 ) val p3 = new Point (3 , 3 ) println(p1.isNotEqual(p2)) println(p1.isNotEqual(p3)) println(p1.isNotEqual(2 )) } }
执行以上代码,输出结果为:
$ scalac Test.scala $ scala Test false true true
特征构造顺序
特征也可以有构造器,由字段的初始化和其他特征体中的语句构成。这些语句在任何混入该特征的对象在构造时都会被执行。
构造器的执行顺序:
调用超类的构造器;
特征构造器在超类构造器之后、类构造器之前执行;
特征由左到右被构造;
每个特征当中,父特征先被构造;
如果多个特征共有一个父特征,父特征不会被重复构造
所有特征被构造完毕,子类被构造。
构造器的顺序是类的线性化的反向。线性化是描述某个类型的所有超类型的一种技术规格。
十六、高级函数 16.1 函数变量 变量类型是函数类型。就有点C++的函数指针的意思。
// 定义一个函数 scala> def add(a:Int, b:Int) = a + b add: (a: Int, b: Int)Int // 将该函数赋值给一个变量(_ 表示取出函数本身) scala> val f = add _ f: (Int, Int) => Int = $$Lambda$1165/1200355641@3e6d80f5 // 通过变量调用 scala> f(1, 2) res123: Int = 3
16.2 匿名函数 没有函数名的函数。一般配合一些参数是函数的函数。
scala> val f = (n: Double) => 3 * n f: Double => Double = $$Lambda$1166/920699871@73c43130 scala> f(5) res124: Double = 15.0
有点C++中的lambda表达式的意思。
scala> Array(1,2,3,4).map(x => x * 2) res128: Array[Int] = Array(2, 4, 6, 8) // 还可以用更简洁的方式 scala> Array(1,2,3,4).map(_ * 2) res127: Array[Int] = Array(2, 4, 6, 8)
16.3 参数是函数的函数 f: (参数类型1[, 参数类型N])=>(结果类型)
scala> def fun(a: Int, b: Int, f: (Int, Int) => Int) = f(a, b) fun: (a: Int, b: Int, f: (Int, Int) => Int)Int scala> def add(a:Int, b:Int) = a + b add: (a: Int, b: Int)Int scala> fun(1, 2, add) res2: Int = 3
16.4 返回值是函数的函数 可以在返回值后加一个()调用
scala> def fun(a: Int, b: Int) = (x: Int) => (a + b) * x fun: (a: Int, b: Int)Int => Int scala> fun(1, 2)(3) res3: Int = 9
其中fun(1, 2)返回了一个新的函数,后面的(3)是新的函数的参数。
十七、控制抽象(分线程) return挺有用的有时候,可以中途返回。
object Thread { def myThread (fun: ()=> Unit ) = { new Thread (){ fun() }.start() } def main (args: Array [String ]): Unit = { myThread(()=> (1 to 5 ).foreach(println)) } }
输出结果:
这里提供一种更简洁的写法:
object Thread { def myThread (fun: => Unit ) = { new Thread (){ fun }.start() } def main (args: Array [String ]): Unit = { myThread{(1 to 5 ).foreach(println)} } }
十八、模式匹配 18.1 模式匹配 值匹配
Scala 提供了强大的模式匹配机制,应用也非常广泛。
一个模式匹配包含了一系列备选项,每个都开始于关键字 case 。每个备选项都包含了一个模式及一到多个表达式。箭头符号 => 隔开了模式和表达式。
val ch = '9 'ch match { case '+' => println("加号" ) case '-' => println("减号" ) case _ => println("数字" ) }
输出结果:
有点类似switch语句,但是不同的是,执行完case 后的语句会直接结束,而不用使用break
类型匹配
val x:Any = "123" ;x match { case b:Int => print("is Int" ) ; case a:String => print("is String" ) ; case _ => print("is Int" ) ; }
输出结果:
18.2 样例类 使用了case关键字 的类定义就是就是样例类(case classes),样例类是种特殊的类,经过优化以用于模式匹配 。
在声明样例类时,下面的过程自动发生了:
构造器的每个参数都成为val,除非显式被声明为var,但是并不推荐这么做;
在伴生对象中提供了apply方法,所以可以不使用new关键字就可构建对象;
提供unapply方法使模式匹配可以工作;
生成toString、equals、hashCode和copy方法,除非显示给出这些方法的定义。
object Test { def main (args: Array [String ]) { val alice = new Person ("Alice" , 25 ) val bob = new Person ("Bob" , 32 ) val charlie = new Person ("Charlie" , 32 ) for (person <- List (alice, bob, charlie)) { person match { case Person ("Alice" , 25 ) => println("Hi Alice!" ) case Person ("Bob" , 32 ) => println("Hi Bob!" ) case Person (name, age) => println("Age: " + age + " year, name: " + name + "?" ) } } } case class Person (name: String , age: Int ) }
执行以上代码,输出结果为:
$ scalac Test.scala $ scala Test Hi Alice! Hi Bob! Age: 32 year, name: Charlie?
sealed 密封样例类
子类和父类必须定义在同一文件中。
sealed abstract class Dog {}case class Jing8 (name:String ) extends Dog {}case class Shapi (age:Int ) extends Dog {}
18.3 PartialFunction 偏函数 val f:PartialFunction [Char ,Int ] = { case '+' => 1 ; case '-' => -1 case _ => 0 } val x = 'a'f(x)
十九、泛型 19.1 类泛型 class Pair [T ,S ](one:T ,second:S ) ; val p = new Pair [String ,Int ]("tom" ,12 ); val p2 = new Pair ("tom" ,12 );
19.2 方法泛型 def getMiddle [T ](arr:Array [T ]) = arr(arr.length / 2 );
19.3 泛型的界限 def run [T <: Dog ](d:T ) = println("hello" )def run2 [T >: Shapi ](d:T ) = println("hello" )<: >: <% T <:Dog >:Cat