Scala —> Java++
- Scala 是面向对象、函数式的编程语言
- Scala 基于 JVM,与 Java 完全兼容,具有跨平台、可移植性、垃圾回收等特性
- Scala 更适合大数据的处理,对集合类型有非常好的支持
Scala 与 Java 与 JVM
Scala 代码的执行过程与 Java 非常类似,先编译,后运行
.scala —> .class 字节码 —> 执行
Hello World
Java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello world!");
}
}
Scala
object HelloWorld {
def main(args: Array[String]): Unit = {
println("hello world")
}
}
object 关键字,声明一个单例对象/伴生对象
main 方法,从外部可以直接调用执行的方法
def 方法名(参数名: 参数类型): 返回值类型 = {
方法体
}
不需要分号
反编译 Hello World
target 目录中一共生成了两份 .class 文件
使用 JavaDecompiler
工具进行反编译可得:
HelloWorld$.class
public final class HelloWorld$ {
public static HelloWorld$ MODULE$;
public void main(String[] args) {
scala.Predef$.MODULE$.println("hello world");
System.out.println("hello from scala");
}
private HelloWorld$() {
MODULE$ = this;
}
}
HelloWorld.class
import scala.reflect.ScalaSignature;
@ScalaSignature(bytes = "\006\001%:Q\001B\003\t\002!1QAC\003\t\002-AQAE\001\005\002MAQ\001F\001\005\002U\t!\002S3mY><vN\0357e\025\0051\021A\002;fgR\004\024g\001\001\021\005%\tQ\"A\003\003\025!+G\016\\8X_JdGm\005\002\002\031A\021Q\002E\007\002\035)\tq\"A\003tG\006d\027-\003\002\022\035\t1\021I\\=SK\032\fa\001P5oSRtD#\001\005\002\t5\f\027N\034\013\003-e\001\"!D\f\n\005aq!\001B+oSRDQAG\002A\002m\tA!\031:hgB\031Q\002\b\020\n\005uq!!B!se\006L\bCA\020'\035\t\001C\005\005\002\"\0355\t!E\003\002$\017\0051AH]8pizJ!!\n\b\002\rA\023X\rZ3g\023\t9\003F\001\004TiJLgn\032\006\003K9\001")
public final class HelloWorld {
public static void main(String[] paramArrayOfString) {
HelloWorld$.MODULE$.main(paramArrayOfString);
}
}
可见,Scala 代码编译时,实际上被编译回了 Java 代码
另外,此处使用了单例模式
变量和常量
Java
// 变量
int a = 1;
// 常量
final int b = 666;
Scala
// 变量
var i = 1;
var i : Int = 1
// 常量
val j = 666
val j : Int = 666
变量类型可省略
Scala 编译器可对当前变量类型作自动推断
变量类型确定后,不可修改
Scala 是强数据类型语言
变量声明时,必须有初始值
区别 var 变量 和 val 常量
标识符命名规范
- 以字母或下划线开头,后接字母、数字、下划线
- 以操作符开头,且仅包含操作符
+ - * / # !
- 用反引号包括的任意字符串,即使是 Scala 关键字也可以
字符串
+
号拼接val name = "alice"; val age = 18; println(name + age)
alice18
*
号多次复制和拼接val name = "alice"; println(name * 3)
alicealicealice
printf 通过
%
传值val name = "alice"; val age = 18 printf("%s is %d years old", name, age)
alice is 18 years old
字符串模板(插值字符串),通过
$
取值// 模板字符串格式 println(s"") println(f"") println(raw"")
val name = "alice"; val age = 18 println(s"${name} is ${age} years old")
alice is 18 years old
三引号,保留多行字符串原格式
val sql = s""" |select * |from | student |where | name = ${name} |""".stripMargin println(sql)
select * from student where name = alice
控制台输入
println("input:")
val str = StdIn.readLine()
println("output:")
println(str)
input:
123
output:
123
读写文件
// 读取文件
Source.fromFile("D:\\IdeaProjects\\ScalaTest\\src\\main\\resources\\test.txt").foreach(print)
// 写入文件
val writer = new PrintWriter(new File("D:\\IdeaProjects\\ScalaTest\\src\\main\\resources\\output.txt"))
writer.write("123456")
writer.close()
数据类型
Java
基本类型:
byte short int long float double char boolean
引用类型:
对象
基本类型对应的包装类 Byte Short Integer Long Float Double Character Boolean
Scala
Scala 中的一切数据都是对象,都是 Any 的子类
Scala 中的数据类型分为数值类型
AnyVal
和引用类型AnyRef
,且两者都是对象Unit 对应 Java 中的 void
用于作为一种特殊的返回值类型,表示方法没有返回值
Unit 是一种数据类型,只有一个对象
()
Null 是一种数据类型,只有一个对象
null
,是所有引用类型AnyRef
的子类Nothing 位于 Scala 类型层次中的最低层,是任何类型的子类
用于作为一种特殊的返回值类型,可将异常返回值抛给其他函数
函数
基本语法
函数定义
// 函数 1:无参,无返回值
def test1(): Unit = {
println("无参,无返回值")
}
test1()
// 函数 2:无参,有返回值
def test2(): String = {
return "无参,有返回值"
}
println(test2())
// 函数 3:有参,无返回值
def test3(s: String): Unit = {
println(s)
}
test3("jinlian")
// 函数 4:有参,有返回值
def test4(s: String): String = {
return s + "有参,有返回值"
}
println(test4("hello "))
// 函数 5:多参,无返回值
def test5(name: String, age: Int): Unit = {
println(s"$name, $age")
}
test5("dalang", 40)
// 函数 6:多参,有返回值
def test6(a: Int, b: Int): Int = {
return a + b;
}
println(test6(666, 777))
函数参数
可变参数
def test(s: String*): Unit = {
println(s)
}
test("hello")
test("a", "b", "c")
test()
WrappedArray(hello)
WrappedArray(a, b, c)
List()
变长参数,返回包装数组
若不传入任何值,则返回List()
若存在多个参数,那么可变参数一般放在最后
参数默认值
def test( name : String, age : Int = 30 ): Unit = {
println(s"$name, $age")
}
test("alice")
test("alice", 18)
alice, 30
alice, 18
若默认值参数传入值,则会覆盖默认值
一般将默认值参数放在最后
带名参数
当函数参数过多时,为了方便调用,可以打乱传入顺序,用参数名指定传入值
或者,当大量参数有默认值,只需要传入某些参数时,会非常方便
def test( name : String, age : Int = 30 ): Unit = {
println(s"$name, $age")
}
test(age = 66, name = "bob")
bob, 66
函数至简原则
// 函数标准写法
def f(s: String): String = {
return s + " abc"
}
println(f("Hello"))
//(1) return 可以省略,Scala 会使用函数体的最后一行代码作为返回值
def f1(s: String): String = {
s + " abc"
}
println(f1("this is f1"))
//(2)如果函数体只有一行代码,可以省略花括号
def f2(s: String): String = s + " abc"
println(f2("this is f2"))
//(3)返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略
def f3(s: String) = s + " abc"
println(f3("this is f3"))
//(4)如果有 return,则不能省略返回值类型,必须指定。
def f4(): String = {
return "this is f4"
}
println(f4())
//(5)如果函数明确声明 unit,那么即使函数体中使用 return 关键字也不起作用
def f5(): Unit = {
return "this is f5"
}
println(f5())
//(6)如果期望是无返回值类型,可以省略等号,将无返回值的函数称之为过程
def f6() {
println("this is f6")
}
f6()
//(7)如果函数无参,但是声明了参数列表,那么调用时小括号可加可不加
def f7() = "this is f7"
println(f7())
println(f7)
//(8)如果函数没有参数列表,那么小括号可以省略,但是调用时小括号必须省略
def f8 = "this is f8"
println(f8)
//(9)如果不关心名称,只关心逻辑处理,那么函数名可以省略
(name: String) => {
println(name)
}
匿名函数
// 一般的匿名函数,Lambda 表达式
val f1 = (name: String) => {
println(name)
}
f1("this is f1")
// 以函数作为参数
def f2(func: String => Unit): Unit = {
func("this is func in f2")
}
f2(f1)
this is f1
this is func in f2
对于 f1,虽然是匿名函数,但仍然可以指定一个函数名
对于 f2,以函数作为参数,参数名是 func
String => Unit
注意此处不是 Lambda 表达式,而是表示 func 的参数类型是 String,返回值类型是 Unit
集合
Scala 中的集合分为三类:Seq 序列、Set 集、Map 映射,所有集合都扩展自 Iterable 特质
对于大部分集合类,Scala 都同时具有可变和不可变两种版本,分别位于 scala.collection.mutable
和scala.collection.immutable
不可变集合
不可变集合是指该集合对象不可修改,一旦发生修改就会返回一个新对象,而原对象保持不变,类似于 Java 中的 String
- Seq 是 Java 当中没有的,其中 List 属于 Seq
- IndexedSeq 和 LinearSeq
- IndexedSeq 通过索引进行查找定位,速度快,如 String 就是一个索引集合
- LinearSeq 为线性集合,一般通过遍历进行查找
可变集合
可变集合是指可以对该集合直接进行修改,不会返回新对象,类似于 Java 中的 StringBuilder
数组 Array
不可变数组
第一种方法定义:
val array = new Array[Int](10)
[Int] 表示元素类型
(10) 表示数组大小
val array = Array(666, 777)
可变数组 ArrayBuffer
val arr = ArrayBuffer[Any](555, 666, 777)
[Any] 表示可存放任意数据类型
//(1)创建并初始赋值可变数组
val arrayBuffer = ArrayBuffer[Any](1, 2, 3)
//(2)遍历数组
for (i <- arrayBuffer) {
println(i)
}
println(arrayBuffer.length) // 3
println("arr01.hash=" + arrayBuffer.hashCode())
//(3)增加元素
// 追加数据
arrayBuffer.+=(4)
// 向数组最后追加数据
arrayBuffer.append(5, 6)
// 向指定的位置插入数据
arrayBuffer.insert(0, 7, 8)
println("arr01.hash=" + arrayBuffer.hashCode())
//(4)修改元素
arrayBuffer(1) = 9 //修改第 2 个元素的值
println("--------------------------")
for (i <- arrayBuffer) {
println(i)
}
println(arrayBuffer.length) // 5
两者相互转换
//不可变数组转可变数组
arr1.toBuffer
//可变数组转不可变数组
arr2.toArray
多维数组
val array = Array.ofDim[Double](3,4)
该二维数组中有 3 个一维数组,每个一维数组有 4 个元素
列表 List
不可变列表
val list1 = List(666, 777)
println(list1)
val list2 = 666 :: 777 :: Nil
println(list2)
List(666, 777)
List(666, 777)
两种方式均可生成相同的列表
双冒号
::
双冒号实际是 Scala 的 List 抽象类中的一个方法
x :: A
或 A.::(x)
作用是插入元素 x 至列表 A 头部,并返回新生成的列表
三冒号
:::
三冒号也是 List 抽象类中的方法
val list1 = List(666, 777)
val list2 = List(888, 999)
println(list1 :: list2)
println(list1 ::: list2)
List(List(666, 777), 888, 999)
List(666, 777, 888, 999)
当列表 A 和列表 B 调用 ::
双冒号时,返回的是一个嵌套结构的新列表
当列表 A 和列表 B 调用 :::
三冒号时,返回的是包含两者所有元素的新列表(扁平化,拆开 + 重组)
可变列表 ListBuffer
//(1)创建一个可变集合
val buffer = ListBuffer(1, 2, 3, 4)
//(2)向集合中添加数据
buffer.+=(5)
buffer.append(6)
buffer.insert(1, 2)
//(3)打印集合数据
buffer.foreach(println)
//(4)修改数据
buffer(1) = 6
buffer.update(1, 7)
//(5)删除数据
buffer.-(5)
buffer.-=(5)
buffer.remove(5)
集 Set
默认情况下,Scala 使用不可变集合,如果想使用可变集合,需要引用 scala.collection.mutable.Set
包
不可变 Set
Set 默认是不可变集合,数据无序,且数据不可重复
val set = Set(1, 2, 3, 4, 5, 6, 3)
for (x <- set) {
println(x)
}
5
1
6
2
3
4
可变 Set
//(1)创建可变集合
val set = mutable.Set(1, 2, 3, 4, 5, 6)
//(2)集合添加元素
set += 8
//(3)向集合中添加元素,返回一个新的 Set
val ints = set.+(9)
println(ints)
println("set2=" + set)
//(4)删除数据
set -= (5)
//(5)打印集合
set.foreach(println)
println(set.mkString(","))
Set(9, 1, 5, 2, 6, 3, 4, 8)
set2=Set(1, 5, 2, 6, 3, 4, 8)
1
2
6
3
4
8
1,2,6,3,4,8
映射 Map
与 Java 类似,Scala 中的 Map 也是一个散列表,存储内容是键值对 key-value
映射
不可变 Map
//(1)创建不可变集合 Map
val map = Map("a" -> 1, "b" -> 2, "c" -> 3)
//(2)访问数据
// 使用 get 访问 map 集合的数据,会返回特殊类型 Option(选项): 有值 (Some ),无值(None)
for (elem <- map.keys) {
println(elem + "=" + map.get(elem).get)
}
//(3)如果 key 不存在,返回 0
println(map.get("d").getOrElse(0))
println(map.getOrElse("d", 0))
//(4)循环打印
map.foreach((kv) => {
println(kv)
})
a=1
b=2
c=3
0
0
(a,1)
(b,2)
(c,3)
可变 Map
//(1)创建可变集合
val map = mutable.Map("a" -> 1, "b" -> 2, "c" -> 3)
//(2)向集合增加数据
// 将数值 4 添加到集合,并把集合中原值 1 返回
map.+=("d" -> 4)
val maybeInt: Option[Int] = map.put("a", 4)
println(maybeInt.getOrElse(0))
//(3)删除数据
map.-=("b", "c")
//(4)修改数据
map.update("d", 5)
map("d") = 5
//(5)打印集合
map.foreach((kv) => {
println(kv)
})
1
(d,5)
(a,4)
元组 Tuple
元组可以理解为一个容器,可以存放各种相同或不同类型的数据
即多个无关的数据封装为一个整体,称为元组
元组中最大只能有 22 个元素
//(1)声明元组的方式:(元素 1,元素 2,元素 3)
val tuple: (Int, String, Boolean) = (40, "bobo", true)
//(2)访问元组
// 通过元素的顺序进行访问,调用方式:_顺序号
println(tuple._1)
println(tuple._2)
println(tuple._3)
// 通过索引访问数据
println(tuple.productElement(0))
// 通过迭代器访问数据
for (elem <- tuple.productIterator) {
println(elem)
}
//(3)Map 中的键值对其实就是元组,只不过元组的元素个数为 2,称之为 对偶
val map = Map("a" -> 1, "b" -> 2, "c" -> 3)
val map1 = Map(("a", 1), ("b", 2), ("c", 3))
map.foreach(tuple => {
println(tuple._1 + "=" + tuple._2)
})
队列 Queue
val queue = new mutable.Queue[String]()
queue.enqueue("a", "b", "c")
println(queue.dequeue())
println(queue.dequeue())
println(queue.dequeue())
a
b
c
并行集合
Scala 为了充分使用多核 CPU,提供了并行集合(有别于前面的串行集合),用于多核环境的并行计算
// 串行
val result1 = (0 to 10).map { case _ =>
Thread.currentThread.getName
}
//并行
val result2 = (0 to 10).par.map { case _ =>
Thread.currentThread.getName
}
println(result1)
println(result2)
Vector(main, main, main, main, main, main, main, main, main, main, main)
ParVector(scala-execution-context-global-12, scala-execution-context-global-12, scala-execution-context-global-14, scala-execution-context-global-14, scala-execution-context-global-14, scala-execution-context-global-13, scala-execution-context-global-16, scala-execution-context-global-16, scala-execution-context-global-15, scala-execution-context-global-15, scala-execution-context-global-15)
集合常用函数
基本操作
val list: List[Int] = List(1, 2, 3, 4, 5, 6, 7)
//(1)获取集合长度
println(list.length)
//(2)获取集合大小,等同于 length
println(list.size)
//(3)循环遍历
list.foreach(println)
//(4)迭代器
for (elem <- list.iterator) {
println(elem)
}
//(5)生成字符串
println(list.mkString(","))
//(6)是否包含
println(list.contains(3))
衍生集合
val list1: List[Int] = List(1, 2, 3, 4, 5, 6, 7)
val list2: List[Int] = List(4, 5, 6, 7, 8, 9, 10)
//(1)获取集合的头
println(list1.head)
//(2)获取集合的尾(除去头的其余所有元素)
println(list1.tail)
//(3)集合最后一个数据
println(list1.last)
//(4)集合初始数据(不包含最后一个)
println(list1.init)
//(5)反转
println(list1.reverse)
//(6)取前(后)n 个元素
println(list1.take(3))
println(list1.takeRight(3))
//(7)去掉前(后)n 个元素
println(list1.drop(3))
println(list1.dropRight(3))
//(8)并集
println(list1.union(list2))
//(9)交集
println(list1.intersect(list2))
//(10)差集
println(list1.diff(list2))
println("===========")
//(11)拉链
// 如果两个集合的元素个数不相等,那么会将同等数量的数据进行拉链 ,多余的数据省略不用
println(list1.zip(list2))
//(12)滑窗
list1.sliding(2, 5).foreach(println)
1
List(2, 3, 4, 5, 6, 7)
7
List(1, 2, 3, 4, 5, 6)
List(7, 6, 5, 4, 3, 2, 1)
List(1, 2, 3)
List(5, 6, 7)
List(4, 5, 6, 7)
List(1, 2, 3, 4)
List(1, 2, 3, 4, 5, 6, 7, 4, 5, 6, 7, 8, 9, 10)
List(4, 5, 6, 7)
List(1, 2, 3)
===========
List((1,4), (2,5), (3,6), (4,7), (5,8), (6,9), (7,10))
List(1, 2)
List(6, 7)
集合计算 简单函数
val list: List[Int] = List(1, 5, -3, 4, 2, -7, 6)
//(1)求和
println(list.sum)
//(2)求乘积
println(list.product)
//(3)最大值
println(list.max)
//(4)最小值
println(list.min)
//(5)排序
// 按照元素大小排序
println(list.sortBy(x => x))
// 按照元素的绝对值大小排序
println(list.sortBy(x => x.abs))
// 按元素大小升序排序
println(list.sortWith((x, y) => x < y))
// 按元素大小降序排序
println(list.sortWith((x, y) => x > y))
8
5040
6
-7
List(-7, -3, 1, 2, 4, 5, 6)
List(1, 2, -3, 4, 5, 6, -7)
List(-7, -3, 1, 2, 4, 5, 6)
List(6, 5, 4, 2, 1, -3, -7)
sorted
对一个集合进行自然排序,通过传递隐式的 Ordering
sortBy
对一个属性或多个属性进行排序,通过它的类型
sortWith
基于函数的排序,通过一个 comparator 函数,实现自定义排序的逻辑
集合计算 高级函数
val list: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9)
val nestedList: List[List[Int]] = List(List(1, 2, 3), List(4, 5, 6), List(7, 8, 9))
val wordList: List[String] = List("hello world", "hello hi", "hello scala")
//(1)过滤
println(list.filter(x => x % 2 == 0))
//(2)转化/映射
println(list.map(x => x + 1))
//(3)扁平化
println(nestedList.flatten)
//(4)扁平化+映射,flatMap 相当于先进行 map 操作,再进行 flatten 操作
// 主要用于分词
println(wordList.flatMap(x => x.split(" ")))
//(5)分组
println(list.groupBy(x => x % 2))
List(2, 4, 6, 8)
List(2, 3, 4, 5, 6, 7, 8, 9, 10)
List(1, 2, 3, 4, 5, 6, 7, 8, 9)
List(hello, world, hello, hi, hello, scala)
Map(1 -> List(1, 3, 5, 7, 9), 0 -> List(2, 4, 6, 8))
Reduce 操作
Reduce 简化(归约)
通过指定规则,将集合中的数据进行聚合,获取结果
val list = List(1,2,3,4)
// 将数据从左往右逐个相减,并基于上一次结果实现运算
// reduce 底层调用的其实就是 reduceLeft
// val i1 = list.reduceLeft((x,y) => x-y)
val i1 = list.reduce((x, y) => x - y)
println("i1 = " + i1)
val i2 = list.reduceRight((x, y) => x - y)
println("i2 = " + i2)
i1 = -8
i2 = -2
Fold 操作
Fold 与 Reduce 操作类似,区别在于 Fold 有初始值,且 foldRight
运算过程不同
val list = List(1, 2, 3, 4)
// fold 方法使用了函数柯里化,存在两个参数列表
// 第一个参数为零值(初始值),第二个参数为简化规则
// fold 底层为 foldLeft
val i1 = list.foldLeft(1)((x, y) => x - y)
val i2 = list.foldRight(10)((x, y) => x - y)
println(i1)
println(i2)
-9
8
注意 i2 的实际运算过程:
1 - (2 - (3 - (4 - 10)))
Map 合并
此处合并是相加,而不是覆盖
// 两个 Map 的数据合并
val map1 = mutable.Map("a" -> 1, "b" -> 2, "c" -> 3)
val map2 = mutable.Map("a" -> 4, "b" -> 5, "d" -> 6)
val map3: mutable.Map[String, Int] = map2.foldLeft(map1) {
(map, kv) => {
val k = kv._1
val v = kv._2
map(k) = map.getOrElse(k, 0) + v
map
}
}
println(map3)
Map(b -> 7, d -> 6, a -> 5, c -> 3)
模式匹配
Scala 中的模式匹配类似于 Java 中的 switch
Java
int i = 10;
switch (i) {
case 10 :
System.out.println("10");
break;
case 20 :
System.out.println("20");
break;
default :
System.out.println("other number");
break;
}
Java 语法中,switch 括号里的值只能是整型、字符型、字符串,即 byte、short、int、char、String 类型
case 后面的值只能是常量、常量表达式,包括整型、字符型、字符串、枚举类型
Scala
val i = 10
i match {
case 10 => println("10")
case 20 => println("20")
case _ => println("other number")
}
Scala 的模式匹配相对于 Java,扩展了更多功能,更加强大
基本语法
var a: Int = 10
var b: Int = 20
var operator: Char = '*'
var result = operator match {
case '+' => a + b
case '-' => a - b
case '*' => a * b
case '/' => a / b
case _ => "illegal"
}
println(result)
200
如果所有 case 都不匹配,那么会执行
case _
分支,类似于 Java 中 default 语句若此时没有
case _
分支,那么会抛出 MatchError每个 case 中,不需要使用 break 语句,自动中断 case
match case 语句可以匹配任何类型,而不只是字面量
=>
后面的代码块,直到下一个 case 语句之前的代码是作为一个整体执行,可以使用{}括起来,也可以不括
模式守卫
如果想要匹配某个范围的数据,就需要增加条件守卫
def abs(x: Int) = x match {
case i: Int if i >= 0 => i
case j: Int if j < 0 => -j
case _ => "type illegal"
}
println(abs(-5))
5
模式匹配扩展
匹配常量
Scala 中模式匹配可以匹配所有的字面量,包括字符串、字符、数字、布尔值等
def main(args: Array[String]): Unit = {
println(describe('+'))
}
def describe(x: Any) = x match {
case 5 => "Int five"
case "hello" => "String hello"
case true => "Boolean true"
case '+' => "Char +"
}
Char +
匹配类型
def describe(x: Any) = x match {
case i: Int => "Int"
case s: String => "String hello"
case m: List[_] => "List"
case c: Array[Int] => "Array[Int]"
case someThing => "something else " + someThing
}
def main(args: Array[String]): Unit = {
//泛型擦除
println(describe(List(1, 2, 3, 4, 5)))
//数组例外,可保留泛型
println(describe(Array(1, 2, 3, 4, 5, 6)))
println(describe(Array("abc")))
}
List
Array[Int]
something else [Ljava.lang.String;@1b604f19
匹配数组
Scala 模式匹配可以对集合进行精确的匹配,例如匹配只有两个元素的,且第一个元素 为 0 的数组
// 对一个数组集合进行遍历
for (arr <- Array(Array(0), Array(1, 0), Array(0, 1, 0), Array(1, 1, 0), Array(1, 1, 0, 1), Array("hello", 90))) {
val result = arr match {
// 匹配 Array(0) 这个数组
case Array(0) => "0"
// 匹配有两个元素的数组,然后将该数组的两个元素值赋给 x, y 并输出
case Array(x, y) => x + "," + y
// 匹配以 0 开头和数组
case Array(0, _*) => "以 0 开头的数组"
case _ => "something else"
}
println("result = " + result)
}
result = 0
result = 1,0
result = 以 0 开头的数组
result = something else
result = something else
result = hello,90
匹配列表
方式一
// lists 是一个存放 List 集合的数组
// 下面的 for 循环依次遍历五个 List,并进行判断
for (lists <- Array(List(0), List(1, 0), List(0, 0, 0), List(1, 0, 0), List(88))) {
val result = lists match {
//匹配 List(0)
case List(0) => "0"
//匹配有两个元素的 List
case List(x, y) => x + "," + y
//匹配 0 开头的数组
case List(0, _*) => "0 ..."
case _ => "something else"
}
println(result)
}
0
1,0
0 ...
something else
something else
方式二
val list: List[Int] = List(1, 2, 5, 6, 7)
list match {
case first :: second :: rest => println(first + "-" +
second + "-" + rest)
case _ => println("something else")
}
1-2-List(5, 6, 7)
匹配元组
// 对一个元组集合进行遍历
for (tuple <- Array((0, 1), (1, 0), (1, 1), (1, 0, 2))) {
val result = tuple match {
// 第一个元素是 0 的元组
case (0, _) => "0 ..."
// 后一个元素是 0 的对偶元组
case (y, 0) => "" + y + "0"
case (a, b) => "" + a + " " + b
case _ => "something else" //默认
}
println(result)
}
0 ...
10
1 1
something else
匹配对象
object Test {
def main(args: Array[String]): Unit = {
val student = new Student("alice", 18)
val result = student match {
case Student("alice", 18) => "name alice, age 18"
case _ => "something else"
}
println(result)
}
}
// 定义 Student 类
class Student(val name: String, val age: Int)
// 定义 Student 伴生对象
object Student {
def apply(name: String, age: Int): Student = new Student(name, age)
// 必须实现 unapply 方法,用于拆解对象属性
def unapply(student: Student): Option[(String, Int)] = {
if (student == null){
None
} else {
Some((student.name, student.age))
}
}
}
name alice, age 18
样例类
上述匹配对象代码比较复杂,样例类对其进行了优化
样例类与一般类相比,可自动生成伴生对象,且伴生对象中自动提供了一些常用方法,如
apply
、unapply
、toString
、equals
、hashCode
和copy
样例类是为模式匹配而优化的类,因为其默认提供了 unapply 方法,因此,样例类可以直接使用模式匹配,而无需自己实现 unapply 方法
构造器中的每一个参数都成为 val,除非它被显式地声明为 var(不建议这样做)
object Test {
def main(args: Array[String]): Unit = {
val user: User = User("zhangsan", 20)
val result = user match {
case User("zhangsan", 20) => "name zhangsan, age 20"
case _ => "no"
}
println(result)
}
}
case class User(name: String, age: Int)
name zhangsan, age 20
异常
Scala 异常处理语法上和 Java 类似,但一些细节有所不同
Java
public class TestJava {
public static void main(String[] args) {
try {
int a = 10;
int b = 0;
int c = a / b;
}catch (ArithmeticException e){
// catch 时,需要将范围小的写到前面
e.printStackTrace();
}catch (Exception e){
e.printStackTrace();
}finally {
System.out.println("finally");
}
}
}
- Java 语言按照 try—catch—finally 的方式来处理异常
- 不管有没有异常捕获,都会执行 finally,因此通常可以在 finally 代码块中释放资 源
- 可以有多个 catch 分别捕获对应的异常,这时需要把范围小的异常类写在前面, 范围大的异常类写在后面,否则编译错误
Scala
object Test {
def main(args: Array[String]): Unit = {
try {
var n = 10 / 0
} catch {
case ex: ArithmeticException => {
// 发生算术异常
println("发生算术异常")
}
case ex: Exception => {
// 对异常处理
println("发生了异常 1")
println("发生了异常 2")
}
} finally {
println("finally")
}
}
}
- 将可疑代码封装在
try
块中,并使用了catch
来捕获异常,若发生异常,则catch
将进行处理,且程序不会终止 - Scala 的异常的工作机制和 Java 一样,但是 Scala 没有
checked
异常(即编译时异常), 异常都是在运行时捕获处理 - 用
throw
关键字抛出异常对象,所有异常都是Throwable
的子类型,另外throw
表达式是有类型的,就是Nothing
隐式转换
当编译器第一次编译失败的时候,会在当前的环境中查找能让代码编译通过的方法,将类型进行转换,实现二次编译
隐式函数
隐式转换可以在不改动任何代码的情况下,扩展某个类的功能
object Test {
def main(args: Array[String]): Unit = {
// 使用 implicit 关键字声明的函数,称之为隐式函数
implicit def convert(arg: Int): MyRichInt = {
new MyRichInt(arg)
}
// 此处通过隐式函数,自动把 2 转换为了 MyRichInt
println(2.myMax(6))
}
}
class MyRichInt(val self: Int) {
def myMax(i: Int): Int = {
if (self < i)
i
else
self
}
def myMin(i: Int): Int = {
if (self < i)
self
else
i
}
}
当想调用对象某一功能时,如果编译错误,那么编译器会尝试在当前作用域内,查找对应功能的转换规则
这个调用过程是由编译器完成的,所以称之为隐式转换,也称之为自动转换
隐式参数
object Test {
def main(args: Array[String]): Unit = {
implicit val str: String = "good world"
def hello(implicit str: String): Unit = {
println(str)
}
// 此处调用 hello 函数并没有主动传入参数,但编译器找到了隐式参数
hello
}
}
good world
泛型
协变和逆变
class MyList[+T]{ //协变
}
class MyList[-T]{ //逆变
}
class MyList[T] //不变
Son
是 Father
的子类
协变:
MyList[Son]
作为MyList[Father]
的子类逆变:
MyList[Son]
作为MyList[Father]
的父类不变:
MyList[Father]
与MyList[Son]
无父子关系
泛型上下限
Class PersonList[T <: Person]{ //泛型上限
}
Class PersonList[T >: Person]{ //泛型下限
}
泛型的上下限的作用是对传入的泛型进行限定
object Test {
def main(args: Array[String]): Unit = {
test(classOf[SubChild])
test[Child](new SubChild)
}
//泛型通配符之上限
def test[A <: Child](a:Class[A]): Unit ={
println(a)
}
//泛型通配符之下限
// def test[A >: Child](a:Class[A]): Unit ={
// println(a)
// }
//泛型通配符之下限 形式扩展
def test[A >: Child](a: A): Unit = {
println(a.getClass.getName)
}
}
class Parent{}
class Child extends Parent{}
class SubChild extends Child{}
class org.example.SubChild
org.example.SubChild
- Post link: http://example.com/2023/01/01/Scala/
- Copyright Notice: All articles in this blog are licensed under unless otherwise stated.