赞
踩
Scala是一门以Java虚拟机(JVM)为运行环境并将面向对象和函数式编程的最佳特性结合在一起的静态类型编程语言(静态语言需要提前编译的如:Java、c、c++等,动态语言如:js)
Scala是一门多范式的编程语言,Scala支持面向对象和函数式编程。(多范式,就是多种编程方法的意思。有面向过程、面向对象、泛型、函数式四种程序设计方法。)
安装对应Java版本的scala 解压 配置系统环境变量
默认情况下 IDEA 不支持 Scala 的开发,需要安装 Scala 插件。可在线安装
默认下,Maven 不支持 Scala 的开发,需要引入 Scala 框架。
helloworld 例子 不用分号
scala 面向对象 object 伴生对象 不是类 会使用$.class 单例方式生成对象 采用伴生对象单例的方式调用方法
HelloWorld.class
HelloWorld$.class
/*
object: 关键字,声明一个单例对象(伴生对象)
*/
object HelloWorld {
/*
main 方法:从外部可以直接调用执行的方法
def 方法名称(参数名称: 参数类型): 返回值类型 = { 方法体 }
*/
def main(args: Array[String]): Unit = {
println("hello world")
System.out.println("hello scala from java")
}
}
main 方法名 scala中声明方法必须采用关键字def声明
小括号 表示参数列表
参数声明方式:java->类型 参数名 scala->参数名 : 类型
方法声明:scala 方法名(参数列表):返回值类型
scala中没有public 关键字,如果不声明访问权限,那么就是公共的
scala中没有静态方法,所以没有static关键字 由object实现类似静态方法的功能**(类名.方法名)**
scala中由 Unit 类型表示没有返回值
Scala 中方法 实现复制给方法声明,所以中间需要等号连接
class 关键字和java 中class关键字作用相同,用来定义一个类
代码规范
class Student(name:String,age:Int){
def printInfo():Unit={
println(name+" "+age+" "+Student.school)
}
}
Object Student{
val school: String="atguigu"
}
常量:在程序执行的过程中,其值不会被改变的变量
注意:能用常量的地方不用变量
快捷键 main 出主方法 def
案例实操
object TestVar {
def main(args: Array[String]): Unit = {
// p1 是 var 修饰的,p1 的属性可以变,而且 p1 本身也可以变
var p1 = new Person()
p1.name = "dalang"
p1 = null
// p2 是 val 修饰的,那么 p2 本身就不可变(即 p2 的内存地址不能变),
//但是,p2 的属性是可以变,因为属性并没有用 val 修饰。
val p2 = new Person()
p2.name="jinlian"
// p2 = null // 错误的,因为 p2 是 val 修饰的
}
}
字符串,通过+号连接
printf 用法:字符串,通过**%传值。** 识别正则
字符串模板(插值字符串):通过 $ 获取变量值
// s"" 表示这是一个模板字符串
多行字符串,在 Scala 中,利用三个双引号包围多行字符串就可以实现。
输入的内容,带有空格、\t 之类,导致每一行的开始位置不能整洁对齐。
应用 scala 的 stripMargin 方法,在 scala 中 stripMargin 默认是 “|” 作为连接符,// 在多行换行的行头前面加一个 “|” 符号即可
object Test04_String { def main(args: Array[String]): Unit = { //(1)字符串,通过+号连接 val name: String = "alice" val age: Int = 18 println(age + "岁的" + name + "在尚硅谷学习") // *用于将一个字符串复制多次并拼接 println(name * 3) //(2)printf用法:字符串,通过%传值。 printf("%d岁的%s在尚硅谷学习", age, name) println() //(3)字符串模板(插值字符串):通过$获取变量值 println(s"${age}岁的${name}在尚硅谷学习") // s直接填进来 val num: Double = 2.3456 println(f"The num is ${num}%2.2f") // 格式化模板字符串 format println(raw"The num is ${num}%2.2f") // raw 原长 // 三引号表示字符串,保持多行字符串的原格式输出 val sql = s""" |select * |from | student |where | name = ${name} |and | age > ${age} |""".stripMargin println(sql) } }
注意: printf 和 println 有区别 printf 能识别正则表达式
在编程中,需要接收用户输入的数据,就可以使用键盘输入语句来获取
StdIn.readLine()、StdIn.readShort()、StdIn.readDouble()
object Test05_StdIn {
def main(args: Array[String]): Unit = {
// 输入信息
println("请输入您的大名:")
val name: String = StdIn.readLine()
println("请输入您的芳龄:")
val age: Int = StdIn.readInt()
// 控制台打印输出
println(s"欢迎${age}岁的${name}来到尚硅谷学习")
}
}
scala.io包
写入数据 调用Java.io 包
object Test06_FileIO {
def main(args: Array[String]): Unit = {
// 1. 从文件中读取数据 函数式编程
Source.fromFile("src/main/resources/test.txt").foreach(print)
// 2. 将数据写入文件
val writer = new PrintWriter(new File("src/main/resources/output.txt"))
writer.write("hello scala from java writer")
writer.close()
}
}
Scala中一切数据都是对象,都是Any的子类。
Scala中数据类型分为两大类:数值类型(AnyVal)、引用类型(AnyRef),不管是值类型还是引用类型都是对象。
Scala数据类型仍然遵守,低精度的值类型向高精度值类型,自动转换(隐式转换)
Scala中的StringOps是对Java中的String增强
Unit:对应Java中的void,用于方法返回值的位置,表示方法没有返回值。Unit是 一个数据类型,只有一个对象就是()。Void不是数据类型,只是一个关键字
Null是一个类型,只 有一个 象就 是null。它是所有引用类型(AnyRef)的子类。
Nothing,是所有数据类型的子类,主要用在一个函数没有明确返回值时使用,因为这样可以把抛出的返回值,返回给任何的变量或者函数。
数据类型 | 描述 |
---|---|
Byte [1] | 8 位有符号补码整数。数值区间为 -128 到 127 |
Short [2] | 16 位有符号补码整数。数值区间为 -32768 到 32767 |
Int [4] | 32 位有符号补码整数。数值区间为 -2147483648 到 2147483647 |
Long [8] | 64 位有符号补码整数。数值区间为 -9223372036854775808 到 9223372036854775807 = 2 的(64-1)次方-1 |
Float [4] | 32 位, IEEE 754 标准的单精度浮点数 |
Double [8] | 64 位 IEEE 754 标准的双精度浮点数 |
\ " :表示 " 双引号
Unit (有返回)类型用来标识过程,也就是没有明确返回值的函数。由此可见,Unit 类似于 Java 里的 void。Unit 只有一个实例——( ),这个实例也没有实质意义
object TestSpecialType {
def main(args: Array[String]): Unit = {
def sayOk:Unit={}
println(sayOk)
}
}
Null(有返回) 类只有一个实例对象,Null 类似于 Java 中的 null 引用。Null 可以赋值给任意引用类型(AnyRef),但是不能赋值给值类型(AnyVal)
object TestDataType {
def main(args: Array[String]): Unit = {
//null 可以赋值给任意引用类型(AnyRef),但是不能赋值给值类型(AnyVal)
var cat = new Cat();
cat = null // 正确
var n1: Int = null // 错误
println("n1:" + n1)
}
}
class Cat{
}
Nothing(不返回),可以作为没有正常返回值的方法的返回类型,非常直观的告诉你这个方法不会正常返回,而且由于 Nothing 是其他任意类型的子类,他还能跟要求返回值的方法兼容。
object TestSpecialType { def main(args: Array[String]): Unit = { def test() : Nothing={ throw new Exception() } test } } object TestSpecialType { def test(n:Int):Int={ if(n==10) throw new Exception() else return n } def main(args: Array[String]): Unit = { println("a") test(10) } }
当程序在进行赋值或者运算时,精度小的类型自动转换为精度大的数值类型,这个就是自动类型转换(隐式转换)
原则
注意:Scala 还提供了非常强大的隐式转换机制(隐式函数,隐式类等),我们放在高级部分专门用一个章节来讲解
自动类型转换的逆过程,将精度大的数值类型转换为精度小的数值类型。使用时要加上强制转函数,但可能造成精度降低或溢出,格外要注意
案例实操
object TestForceTransfer {
def main(args: Array[String]): Unit = {
var n1:Int=2.5.toInt
// 强转符号只针对于最近的操作数有效,往往会使用小括号提升优先级
var r1: Int = 10 * 3.5.toInt + 6 * 1.5.toInt // 10 *3 + 6*1 = 36 降低了
var r2: Int = (10 * 3.5 + 6 * 1.5).toInt // 44.0.toInt =44
println("r1=" + r1 + " r2=" + r2)
}
}
基本类型转 String 类型(语法:将基本类型的值+“” 即可)
String 类型转基本数值类型(语法:s1.toInt、s1.toFloat、s1.toDouble、s1.toByte、s1.toLong、s1.toShort)
object Test08_DataTypeConversion { def main(args: Array[String]): Unit = { // 3. 数值类型和String类型的转换 // (1) 数值转String val n: Int = 27 val s: String = n + "" println(s) // (2) String转数值 val m: Int = "12".toInt val f: Float = "12.3".toFloat val f2: Int = "12.3".toDouble.toInt println(f2) } }
package chapter02 /* 128: Int类型,占据4个字节,32位 原码 0000 0000 0000 0000 0000 0000 1000 0000 补码 0000 0000 0000 0000 0000 0000 1000 0000 截取最后一个字节,Byte 8位 得到补码 1000 0000 第一个1 是符号位 表示最大负数 -128 130: Int类型,占据4个字节,32位 原码 0000 0000 0000 0000 0000 0000 1000 0010 补码 0000 0000 0000 0000 0000 0000 1000 0010 截取最后一个字节,Byte 得到补码 1000 0010 -128+2 对应原码 1111 1110 取反加一 -126 */ object Test09_Problem_DataTypeConversion { def main(args: Array[String]): Unit = { var n: Int = 130 val b: Byte = n.toByte println(b) } }
对于除号“/”,它的整数除和小数除是有区别的:整数之间做除法时,只保留整数部分而舍弃小数部分
object Test01_TestOperator {
def main(args: Array[String]): Unit = {
val result1: Int = 10 / 3
println(result1) //3
val result2: Double = 10 / 3
println(result2) // 3.0
val result3: Double = 10.0 / 3 // 3.33
println(result3.formatted("%5.2f")) // 含义:保留小数点 2 位,使用四舍五入
}
}
Scala:==更加类似于 Java 中的 equals,参照 jd 工具 scala 里的string 就是java的string
eq // false 判断对象的引用地址
object Test01_TestOperator {
def main(args: Array[String]): Unit = {
val s1: String = "hello"
val s2: String = new String("hello")
println(s1 == s2) // true
println(s1.equals(s2)) // true
println(s1.eq(s2)) // false 判断对象的引用地址
}
}
Scala 中没有++、–操作符,可以通过+=、-=来实现同样的效果;
object TestAssignment {
def main(args: Array[String]): Unit = {
var r1 = 10
r1 += 1 // 没有++
r1 -= 2 // 没有--
} }
扩展避免逻辑与 && 空指针异常
object Test01_TestOperator {
def main(args: Array[String]): Unit = {
// 判断一个字符串是否为空 错误示范
def isNotEmpty(str: String): Boolean = {
return !(str=="") // 如果传进来的是null 不应该报错 应该false
}
// 判断一个字符串是否为空
def isNotEmpty(str: String): Boolean = {
return str != null && !("".equals(str.trim))
}
}
}
其他和Java运算符差不多
在 Scala 中其实是没有运算符的,所有运算符都是方法。
object TestOpt {
def main(args: Array[String]): Unit = {
// 标准的加法运算
val i:Int = 1.+(1)
// (1)当调用对象的方法时,.可以省略
val j:Int = 1 + (1)
// (2)如果函数参数只有一个,或者没有参数,()可以省略
val k:Int = 1 + 1
println(1.toString())
println(1 toString())
println(1 toString)
} }
需求:Scala 中 if else 表达式其实是有返回值的,具体返回值取决于满足条件的代码体的最后一行内容
object TestIfElse {
def main(args: Array[String]): Unit = {
println("input age");
var age = StdIn.readInt();
val res: String = if (age < 18) {
"童年"
} else if (age >= 18 && age < 30) {
"中年"
} else {
"老年"
}
println(res)
}
}
需求:scala 中返回值类型不一致,取它们共同的祖先
object TestIfElse {
def main(args: Array[String]): Unit = {
println("input age");
var age = StdIn.readInt();
val res: Any = if (age < 18) {
"童年"
} else if (age >= 18 && age < 30) {
"中年"
} else {
100
}
println(res)
}
}
需求3:java中的三元运算符可以用if else实现
如果大括号{}内的逻辑代码只有一行,大括号可以省略。如果省略大括号,if 只对最近的一行逻辑代码起作用。
object TestIfElse {
def main(args: Array[String]): Unit = {
println("input age");
var age = StdIn.readInt();
val res: Any = if (age < 18) "童年" else "成年"
// 不起作用
println(res)
}
}
在 Scala 中没有 Switch,而是使用模式匹配来处理。
模式匹配涉及到的知识点较为综合,因此我们放在后面讲解
Scala 也为 for 循环这一常见的控制结构提供了非常多的特性,这些 for 循环的特性被称为 for 推导式或 for 表达式
基本语法
i 表示循环的变量,<- 规定 to
i 将会从 1-5循环,前后闭合
object TestFor {
def main(args: Array[String]): Unit = {
for (i <- 1 to 5){
println("to")
}
}
}
这种方式和前面的区别在于 i 是从 1 到 5-1 =4 即前闭合后开的范围
object TestFor {
def main(args: Array[String]): Unit = {
for (i <- 1 until 5){
println("until")
}
}
}
循环守卫,即循环保护式(也称条件判断式,守卫)。保护式为 true 则进入循环体内部,为 false 则跳过,类似于 continue
for(i <- 1 to 3 if i != 2) {
print(i + " ")
}
println()
等价于
for (i <- 1 to 3){
if (i != 2) {
print(i + " ")
} }
for (i <- 1 to 10 by 2) {
println("i=" + i)
}
for(i <- 1 to 3; j <- 1 to 4 {
println(" i =" + i + " j = " + j)
}
i=1 j=1
i=1 j=2
i=1 j=3
i=1 j=4
i=2 j=1
i=2 j=2
i=2 j=3
i=2 j=4
i=3 j=1
i=3 j=2
i=3 j=3
i=3 j=4
等价于
for (i <- 1 to 3) {
for (j <- 1 to 3) {
println("i =" + i + " j=" + j)
} }
for(i <- 1 to 3; j = 4 - i) {
println("i=" + i + " j=" + j)
}
for {
i <- 1 to 3
j = 4 - i
} {
println("i=" + i + " j=" + j)
}
等价于
for (i <- 1 to 3) {
var j = 4 - i
println("i=" + i + " j=" + j)
}
注意:开发中很少使用。
说明:将遍历过程中处理的结果返回到一个新 Vector 集合中,使用 yield 关键字
val res = for(i <- 1 to 10) yield i*2
println(res)
Vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)
for(i <- 1 to 10 reverse){
println(i)
}
与 for 语句不同,while 语句没有返回值,即整个 while 语句的结果是 Unit 类型()
do…while 循环是先执行,再判断
object TestWhile {
def main(args: Array[String]): Unit = {
var i=0
do{
println("狗头")
i+=1
}while(i<10)
}
}
Scala 内置控制结构特地去掉了 break 和 continue
Scala中使用breakable控制结构来实现 break 和 continue 功能。
object TestBreak {
try {
for (elem <- 1 to 10) {
println(elem)
if (elem == 5) throw RuntimeException
}
} catch {
case e =>
}
println("正常结束循环")
}
需求2:采用Scala自带的函数,退出循环
object TestBreak {
def main(args: Array[String]): Unit = {
Breaks.breakable {
for (elem <- 1 to 10) {
println(elem)
if (elem == 5) Breaks.break()
}
}
println("正常结束循环")
}
}
需求3: 对break 进行省略
import scala.util.control.Breaks._
object TestBreak {
def main(args: Array[String]): Unit = {
breakable {
for (elem <- 1 to 10) {
println(elem)
if (elem == 5) break()
}
}
println("正常结束循环")
}
}
基本语法
(1)为完成某一功能的程序语句的集合,称为函数。
(2)类中的函数称之方法。
函数没有重载和重写的概念;方法可以进行重载和重写
package TestFunction object TestFunction { def main(): Unit = { } // 方法可以进行重载和重写,程序可以执行 def main(args: Array[String]): Unit = { // scala 语言可以在任何语法中声明任何语法 import java.util.Date new Date() // 函数没有重载和重写的概念,程序报错 // def test(): Unit = { // println("无参,无返回值") // } def test(name: String): Unit = { println("无参,无返回值") } // Scala 中函数可以嵌套定义 def test2():Unit={ def test3(name:String):Unit={ println("函数可以嵌套定义") } } } }
object TestFunction { def main(args: Array[String]): Unit = { def test(s: String*): Unit = { println(s) } test("Hello","Scala") test() // (2)如果参数列表中存在多个参数,那么可变参数一般放置在最后 def test2( name : String, s: String* ): Unit = { println(name + "," + s) } test2("jinlian", "dalang") // (3)参数默认值 def test3( name : String, age : Int = 30 ): Unit = { println(s"$name, $age") } // 如果参数传递了值,那么会覆盖默认值 test3("jinlian", 20) // 如果参数有默认值,在调用的时候,可以省略这个参数 test3("dalang") // 一般情况下,将有默认值的参数放置在参数列表的后面 def test4( sex : String = "男", name : String ): Unit = { println(s"$name, $sex") } // Scala 函数中参数传递是,从左到右 //test4("wusong") //(4)带名参数 test4(name="ximenqing") } }
object TestFunction2 { def main(args: Array[String]): Unit = { // 标准写法 def f(s: String): String = { return s + "jj" } println(f("h")) //(1) return 可以省略,Scala 会使用函数体的最后一行代码作为返回值 def f2(s: String): String = { s + "jj" } println(f2("h")) //(2)如果函数体只有一行代码,可以省略花括号 def f3(s: String): String = s + "jj" //(3)返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略) def f4( s : String ) = s + " jinlian" println(f4("Hello3")) //(4)如果有 return,则不能省略返回值类型,必须指定 def f5():String={ return "sda" } //(5)如果函数明确声明 unit,那么即使函数体中使用 return 关键字也不起作用 def f6(): Unit ={ return "dada" } println(f6()) // unit //(6)Scala 如果期望是无返回值类型,可以省略等号 // 将无返回值的函数称之为过程 def f7(){ "dada" } println(f7()) // (7)如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加 def f8()="wucan" println(f8) // (8)如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略 def f9 = "wuliebiao" println(f9) // (9)如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略 def f10= (x:String)=>{println("wusong")} def f11(f:String=>Unit) = { f("") } f11(f10) println(f11((x:String)=>{println("wusong")})) } }
object TestFunction3 { def main(args: Array[String]): Unit = { //(1)调用 foo 函数,把返回值给变量 f //val f = foo() val f = foo println(f) //(2)在被调用函数 foo 后面加上 _,相当于把函数 foo 当成一个整体, 传递给变量 f1 val f1 = foo _ f1() //(3)如果明确变量类型,那么不使用下划线也可以将函数作为整体传递给 变量 var f2:()=>Int=foo } def foo():Int={ println("floo..") 1 } }
函数可以作为参数进行传递
object TestFunction5 {
def main(args: Array[String]): Unit = {
// (1)定义一个函数,函数参数还是一个函数签名;f 表示函数名称;(Int,Int) 表示输入两个 Int 参数;Int 表示函数返回值
def f1(f:(Int,Int)=>Int):Int={
f(2,4)
}
// (2)定义一个函数,参数和返回值类型和 f1 的输入参数一致
def add(a:Int,b:Int):Int=a+b
// (3)将 add 函数作为参数传递给 f1 函数,如果能够推断出来不是调用,_ 可以省略
println(f1(add))
println(f1(add _))
//可以传递匿名函数
}
}
函数可以作为函数返回值返回
def ff()={
def f2()={
}
f2 _
}
val fo=ff()
// 因为 f1 函数的返回值依然为函数,所以可以变量 f 可以作为函数继续调用
fo()
// 上面的代码可以简化为
ff()()
没有名字的函数就是匿名函数。
(x:Int)=>{函数体}
x:形参;Int:表示输入参数类型;函数体:表示具体代码逻辑
(1)参数的类型可以省略,会根据形参进行自动的推导
(2)类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参
数超过 1 的永远不能省略圆括号。
(3)匿名函数如果只有一行,则大括号也可以省略
(4)如果参数只出现一次,则参数省略且后面参数可以用_代替
object TestFunction4 { def main(args: Array[String]): Unit = { // (1)定义一个函数:参数包含数据和逻辑函数 def operation(arr: Array[Int], op: Int => Int) = { for (elem <- arr) yield op(elem) } // (4)采用匿名函数 val arr=operation(Array(1,2,3,4),(ele:Int)=>{ ele+1 }) println(arr.mkString(",")) // (4.1)参数的类型可以省略,会根据形参进行自动的推导; val arr2=operation(Array(1,2,3,4),(ele)=>{ ele+1 }) println(arr2.mkString(",")) // (4.2)类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参数超过 1 的永远不能省略圆括号。 val arr3=operation(Array(1,2,3,4),ele=>{ ele+1 }) println(arr3.mkString(",")) // (4.3) 匿名函数如果只有一行,则大括号也可以省略 val arr4=operation(Array(1,2,3,4),ele=>ele+1) println(arr4.mkString(",")) // (4.3) 匿名函数如果只有一行,则大括号也可以省略 val arr5=operation(Array(1,2,3,4),_+1) println(arr5.mkString(",")) } }
需求:模拟 Map 映射、Filter 过滤、Reduce 聚合
package study04 import scala.collection.mutable.ArrayBuffer object TestFunction6 { def main(args: Array[String]): Unit = { // (1)map 映射 def map(array: Array[Int], op: Int => Int) = { for (elem <- array) yield op(elem) } val arr = map(Array(1, 2, 3, 4), (x: Int) => { x * x }) println(arr.mkString(",")) } // (2)filter 过滤。有参数,且参数再后面只使用一次,则参数省略且 后面参数用_表示 def filter(arr: Array[Int], op: Int => Boolean) = { var arr1: ArrayBuffer[Int] = ArrayBuffer[Int]() for (elem <- arr if op(elem)) { arr1.append(elem) } arr1.toArray } var arr1 = filter(Array(1, 2, 3, 4), _ % 2 == 1) println(arr1.mkString(",")) // (3)reduce 聚合。有多个参数,且每个参数再后面只使用一次,则参数省略且后面参数用_表示,第 n 个_代表第 n 个参数 def reduce(array: Array[Int], op: (Int, Int) => Int) = { var init: Int = array(0) for (elem <- 1 until array.length){ init=op(init,elem) } init } //val arr2 = reduce(Array(1, 2, 3, 4), (x, y) => x * y) val arr2 = reduce(Array(1, 2, 3, 4), _ * _) println(arr2) } 6
说明
闭包:如果一个函数,访问到了它的外部(局部)变量的值,那么这个函数和他所处的环境,称为闭包
函数柯里化:把一个参数列表的多个参数,变成多个参数列表。
object TestFunction { def main(args: Array[String]): Unit = { def f1()={ var a:Int = 10 def f2(b:Int)={ a + b } f2 _ } // 在调用时,f1 函数执行完毕后,局部变量 a 应该随着栈空间释放掉 val f = f1() // 但是在此处,变量 a 其实并没有释放,而是包含在了 f2 函数的内部,形 成了闭合的效果 println(f(3)) println(f1()(3)) // 函数柯里化,其实就是将复杂的参数逻辑变得简单化,函数柯里化一定存 在闭包 def f3()(b:Int)={ a + b } println(f3()(3)) } }
一个函数/方法在函数/方法体内又调用了本身,我们称之为递归调用
object TestFunction { def main(args: Array[String]): Unit = { // 阶乘 // 递归算法 // 1) 方法调用自身 // 2) 方法必须要有跳出的逻辑 // 3) 方法调用自身时,传递的参数应该有规律 // 4) scala 中的递归必须声明函数返回值类型 println(test(5)) } def test(i : Int) : Int = { if (i == 1) { 1 } else { i * test(I - 1) } } }
注意:Java 只有值调用;Scala 既有值调用,又有名调用
值调用:把计算后的值传递过去
object TestControl {
def main(args: Array[String]): Unit = {
def f = ()=>{
println("f...")
10
}
foo(f())
}
def foo(a: Int):Unit = {
println(a)
println(a)
}
}
名调用:把代码传递过去
object TestControl {
def main(args: Array[String]): Unit = {
def f = () => {
println("f...")
10
}
foo(f())
}
def foo(a: => Int): Unit = {
println(a)
println(a)
}
}
实操
自定义while
object TestControl { def main(args: Array[String]): Unit = { var i:Int=1 myWhile(i<=10){ println(i) i+=1 } } def myWhile(condition: =>Boolean)(op: =>Unit):Unit={ if(condition){ op myWhile(condition)(op) } } }
当函数返回值被声明为 lazy 时,函数的执行将被推迟,直到我们首次对此取值,该函数才会执行。这种函数我们称之为惰性函数
注意:lazy 不能修饰 var 类型的变量
object TestFunction7 { def sum(n1:Int,n2:Int):Int={ println("sum 执行----") return n1+n2 } def main(args: Array[String]): Unit = { lazy val res=sum(10,2) println("-----") println("res= "+res) } } ----- sum 执行---- res= 12
包的命名
com.公司名.项目名.业务模块名
包说明
第二种风格 嵌套式风格表示层级关系
package com{
package atguigu{
package scala{
}
}
}
第二种风格有以下特点:
(1)一个源文件中可以声明多个 package
(2)子包中的类可以直接访问父包中的内容,而无需导包 父包需要
package com{ import com.atguigu.scala.Inner // 在外层包中定义单例对象 object Outer{ var out: String = "out" def main(args: Array[String]): Unit = { println(Inner.in) } } package atguigu{ package scala{ // 内层包中定义单例对象 object Inner{ var in: String = "in" def main(args: Array[String]): Unit = { println(Outer.out) Outer.out = "outer" println(Outer.out) } } } } }
在 Scala 中可以为每个包定义一个同名的包对象,定义在包对象中的成员,作为其对应包下所有 class 和 object 的共享变量,可以被直接访问
若使用 Java 的包管理风格,则包对象一般定义在其对应包下的 package.scala文件中,包对象名与包名保持一致。
如采用嵌套方式管理包,则包对象可与包定义在同一文件中,但是要保证包对象与包声明在同一作用域中
package com{
object Outer{
val out:String="out"
def main(args:ArrayList[String]):Unit={
println(name)
}
}
}
package object com{
val name:String = "com"
}
注意
Scala 中的三个默认导入分别是
import java.lang._
import scala._
import scala.Predef._
注意:Scala 中没有 public,一个.scala 中可以写多个类。
[修饰符] class 类名 {
类体
}
[修饰符] var|val 属性名称 [:类型] = 属性值
注:Bean 属性(@BeanPropetry),可以自动生成规范的 setXxx/getXxx 方法
class Person { var name: String = "bob" var age: Int = _ // _ 表示给属性一个默认值 // Bean 属性 @BeanProperty var sex:String= "男" // val 修饰的属性不能赋默认值,必须显式指定 } object Person{ def main(args: Array[String]): Unit = { var person=new Person() println(person.name) person.setSex("女") println(person.getSex) } }
封装就是把抽象出的数据和对数据的操作封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员方法),才能对数据进行操作。Java 封装操作如下,
(1)将属性进行私有化
(2)提供一个公共的 set 方法,用于对属性赋值
(3)提供一个公共的 get 方法,用于获取属性的值
Scala 中的 public 属性,底层实际为 private,并通过 get 方法(obj.field())和 set 方法(obj.field_=(value))对其进行操作。所以 Scala 并不推荐将属性设为 private,再为其设置public 的 get 和 set 方法的做法。但由于很多 Java 框架都利用反射调用 getXXX 和 setXXX 方法,有时候为了和这些框架兼容,也会为 Scala 的属性设置 getXXX 和 setXXX 方法**(通过@BeanProperty 注解实现)**
(1)Scala 中属性和方法的默认访问权限为 public,但 Scala 中无 public 关键字。
(2)private 为私有权限,只在类的内部和伴生对象中可用。
(3)protected 为受保护权限,Scala 中受保护权限比 Java 中更严格,同类、子类可以访问,同包无法访问。
(4)private[包名]增加包访问权限,包名下的其他类也可以使用
def 方法名 (参数列表) [:返回值类型] = {
方法体
}
val | var 对象名 [:类型] = new 类型()
(1)val 修饰对象,不能改变对象的引用(即:内存地址),可以改变对象属性的值。
(2)var 修饰对象,可以修改对象的引用和修改对象的属性值
(3)自动推导变量类型不能多态,所以多态需要显示声明
class Person { var name: String = "bob" var age: Int = _ // _ 表示给属性一个默认值 // Bean 属性 @BeanProperty var sex:String= "男" // val 修饰的属性不能赋默认值,必须显式指定 } object Person{ def main(args: Array[String]): Unit = { //val 修饰对象,不能改变对象的引用(即:内存地址),可以改变对象属性的值 val person=new Person() person.name="cang" // person=new Person() println(person.name) } }
Scala 构造对象也需要调用构造方法,并且可以有任意多个构造方法。
Scala 类的构造器包括:主构造器和辅助构造器
class 类名**(形参列表)** { // 主构造器
// 类体
def this(形参列表) { // 辅助构造器
}
def this(形参列表) { //辅助构造器可以有多个…
}
}
// 1 如果主构造器无参数 小括号可省略 class Person { // this() 主构造器 var name:String=_ var age:Int=_ def this(age:Int){ this() this.age=age println("辅助构造器") } def this(age:Int,name:String){ this(age) this.name=name } println("主构造器") } object Person{ def main(args: Array[String]): Unit = { val person2=new Person(18); } }
说明:
(1)辅助构造器,函数的名称 this,可以有多个,编译器通过参数的个数及类型来区分。
(2)辅助构造方法不能直接构建对象,必须直接或者间接调用主构造方法。
(3)构造器调用其他另外的构造器,要求被调用构造器必须提前声明
Scala 类的主构造器函数的形参包括三种类型:未用任何修饰、var 修饰、val 修饰
(1)未用任何修饰符修饰,这个参数就是一个局部变量
(2)var 修饰参数,作为类的成员属性使用,可以修改
(3)val 修饰参数,作为类只读属性使用,不能修改
class Person(name:String,var age:Int,val sex:String){ } object Test{ def main(args: Array[String]): Unit = { var person=new Person("bobo",19,"男") //(1) 未用任何修饰符修饰,这个参数就是一个局部变量 // printf(person.name) //(2) var修饰参数 作为类的成员属性使用,可以修改 person.age=20 println(person.age) // (3) val 修饰参数,作为类的只读属性使用,不能修改 // person.sex="女" println(person.sex) } }
class 子类名 extends 父类名 { 类体 }
(1)子类继承父类的属性和方法
(2)scala 是单继承
(3)继承的调用顺序:父类构造器->子类构造器
class Person(nameParam: String) { var name = nameParam var age: Int = _ def this(nameParam: String, ageParam: Int) { this(nameParam) this.age = ageParam println("父类辅助构造器") } println("父类主构造器") } class Emp(nameParam: String, ageParam: Int) extends Person(nameParam, ageParam) { var empNo: Int = _ def this(nameParam: String, ageParam: Int, empNoParam: Int) { this(nameParam, ageParam) this.empNo=empNoParam println("子类的辅助构造器") } println("子类主构造器") } object Test{ def main(args: Array[String]): Unit = { new Emp("z3",11,1001) } }
运行时 绑定方法
scala override 关键字
class Person { val name: String = "person" def hello(): Unit = { println("hello person") } } class Teacher extends Person { override val name: String = "teacher" override def hello(): Unit = { println("hello teacher") } } object Test { def main(args: Array[String]): Unit = { val teacher: Teacher = new Teacher() println(teacher.name) teacher.hello() val teacher1:Person = new Teacher println(teacher1.name) teacher1.hello() } }
class Person { public String name = "person"; public void hello() { System.out.println("hello person"); } } class Teacher extends Person { public String name = "teacher"; @Override public void hello() { System.out.println("hello teacher"); } } public class TestDynamic { public static void main(String[] args) { Teacher teacher = new Teacher(); Person teacher1 = new Teacher(); System.out.println(teacher.name); teacher.hello(); System.out.println(teacher1.name); teacher1.hello(); } }
Scala 中属性和方法都是动态绑定,而 Java 中只有方法为动态绑定,属性静态绑定(编译时绑定好了)。
(1)定义抽象类:abstract class Person{} //通过 abstract 关键字标记抽象类
(2)定义抽象属性:val|var name:String //一个属性没有初始化,就是抽象属性
(3)定义抽象方法:def hello():String //只声明而没有实现的方法,就是抽象方法
abstract class Person {
val name: String
def hello(): Unit
}
class Teacher extends Person {
val name: String = "teacher"
def hello(): Unit = {
println("hello teacher")
}
}
如果父类为抽象类,那么子类需要将抽象的属性和方法实现,否则子类也需声明为抽象类
重写非抽象方法需要用 override 修饰,重写抽象方法则可以不加 override。
子类中调用父类的方法使用 super 关键字
子类对抽象属性进行实现,父类抽象属性可以用 var 修饰;
子类对非抽象属性重写,父类非抽象属性只支持 val 类型,而不支持 var。
因为 var 修饰的为可变变量,子类继承之后就可以直接使用,没有必要重写
可以通过包含带有定义或重写的代码块的方式创建一个匿名的子类
abstract class Person {
val name: String = "person"
def hello():Unit
}
object Test{
def main(args: Array[String]): Unit = {
val person=new Person {
override val name: String = "teacher"
override def hello(): Unit = println("hello teacher")
}
}
}
object Person{
val country:String=“China”
}
若单例对象名与类名一致,则称该单例对象这个类的伴生对象,这个类的所有“静态”内容都可以放置在它的伴生对象中声明
// (1) 伴生对象采用object 关键字声明 object Person{ var country:String="china" } //(2)伴生对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。 class Person { var name: String = "bobo" } object Test{ def main(args: Array[String]): Unit = { //(3)伴生对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。 println(Person.country) } }
(1)单例对象采用 object 关键字声明
(2)单例对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。
(3)单例对象中的属性和方法都可以通过**伴生对象名(类名)**直接调用访问
(1)通过伴生对象的 apply 方法,实现不使用 new 方法创建对象。
(2)如果想让主构造器变成私有的,可以在()之前加上 private。
(3)apply 方法可以重载。
(4)Scala 中 **obj(arg)**的语句实际是在调用该对象的 apply 方法,即 obj.apply(arg)。用以统一面向对象编程和函数式编程的风格。
(5)当使用 new 关键字构建对象时,调用的其实是类的构造方法,当直接使用类名构建对象时,调用的其实时伴生对象的 apply 方法。
// (1) 伴生对象采用object 关键字声明 object Person{ def apply():Person={ println("apply 空参被调用") new Person("xx") } //注意:也可以创建其它类型对象,并不一定是伴生类对象 def apply(name: String): Person = { println("apply 有参被调用") new Person(name) } } //(2)如果想让主构造器变成私有的,可以在()之前加上 private class Person private(cName:String){ var name: String = cName } object Test{ def main(args: Array[String]): Unit = { //(1)通过伴生对象的 apply 方法,实现不使用 new 关键字创建对象 val p1=Person() println("p1.name="+p1.name) val p2 = Person("bobo") println("p2.name=" + p2.name) } }
Scala 语言中,采用特质 trait(特征)来代替接口的概念,也就是说,多个类具有相同的特质(特征)时,就可以将这个特质(特征)独立出来,采用关键字 trait 声明。
Scala 中的 trait 中即可以有抽象属性和方法,也可以有具体的属性和方法,一个类可以混入(mixin)多个特质。这种感觉类似于 Java 中的抽象类
trait 特质名 {
trait 主体
}
通过查看字节码,可以看到特质=抽象类+接口
trait PersonTrait {
// 声明属性
var name:String=_
// 声明方法
def eat():Unit={
}
// 抽象属性
var age:Int
// 抽象方法
def say():Unit
}
一个类具有某种特质(特征),就意味着这个类满足了这个特质(特征)的所有要素,所以在使用时,也采用了 extends 关键字,如果有多个特质或存在父类,那么需要采用 with关键字连接
基本语法:
说明
案例实操
trait PersonTrait { //(1)特质可以同时拥有抽象方法和具体方法 // 声明属性 var name: String = _ // 抽象属性 var age: Int // 声明方法 def eat(): Unit = { println("eat") } // 抽象方法 def say(): Unit } trait SexTrait{ var sex:String } //(2)一个类可以实现/继承多个特质 //(3)所有的 Java 接口都可以当做 Scala 特质使用 class Teacher extends PersonTrait with java.io.Serializable { override def say(): Unit = { println("say") } override var age: Int = _ } object TestTrait{ def main(args: Array[String]): Unit = { val teacher = new Teacher teacher.say() teacher.eat() //(4)动态混入:可灵活的扩展类的功能 val t2 = new Teacher with SexTrait { override var sex: String = "男" } //调用混入 trait 的属性 println(t2.sex) } }
由于一个类可以混入(mixin)多个 trait,且 trait 中可以有具体的属性和方法,若混入的特质中具有相同的方法(方法名,参数列表,返回值均相同),必然会出现继承冲突问题。冲突分为以下两种:
package study05 trait Ball{ def describe():String={ "ball" } } trait Color extends Ball{ override def describe(): String = { "blue-"+super.describe() } } trait Catagory extends Ball{ override def describe(): String = { "foot-"+super.describe() } } class MyBall extends Catagory with Color { override def describe(): String = { "my ball is a "+super.describe() } } object TestTrait02{ def main(args: Array[String]): Unit = { println(new MyBall().describe()) } }
结果
my ball is a blue-foot-ball
**思考:**上述案例中的 super.describe()调用的是父 trait 中的方法吗?
当一个类混入多个特质的时候,scala 会对所有的特质及其父特质按照一定的顺序进行排序**,而此案例中的 super.describe()调用的实际上是排好序后的下一个特质中的 describe()方法**。,排序规则如下
- 案例中的 super,不是表示其父特质对象,而是表示上述叠加顺序中的下一个特质,即,MyClass 中的 super 指代 Color,Color 中的 super 指代 Category,Category 中的 super 指代 Ball。
- 如果想要调用某个指定的混入特质中的方法,可以增加约束:super[],例如 super[Category].describe()
自身类型可实现依赖注入的功能
// 用户类 class User(val name: String, val password: String) trait UserDao { // 没看懂 // 这就依赖注入啦 // 自身类型命名为_ 并且限定为UserDao 实例化 必须携带user _: User => // 向数据库插入数据 def insert(): Unit = { println(s"insert into db: ${this.name}") } } // 定义注册用户类 class RegisterUser(name: String, password: String) extends User(name, password) with UserDao object TestUser{ def main(args: Array[String]): Unit = { val user = new RegisterUser("alice", "123456") user.insert() } }
枚举类:需要继承 Enumeration
应用类:需要继承 App
object他继承了App类,没有写main方法,但是可以生产运行,这是由于App类是Scala自己提供的一个类,它的作用是
当object继承它时,不需要写main方法,而是将整个的类看做一个main方法, 可以直接运行
object testApp extends App { println("app start") type MyString = String val a: MyString = "abc" println(a) } // 枚举类 object EnColor extends Enumeration{ val RED=Value(1,"red") val YELLOW=Value(2,"yellow") val BLUE=Value(3,"blue") } object TestColor{ def main(args: Array[String]): Unit = { println(EnColor.RED) } }
使用 type 关键字可以定义新的数据数据类型名称,本质上就是类型的一个别名
object TestColor{
def main(args: Array[String]): Unit = {
type S=String
var v:S="abc"
def test():S="xyz"
}
}
Scala 的集合有三大类:序列 Seq、集 Set、映射 Map,所有的集合都扩展自 Iterable特质
对于几乎所有的集合类,Scala 都同时提供了可变和不可变的版本,分别位于以下两个包
Scala 不可变集合,就是指该集合对象不可修改,每次修改就会返回一个新对象,而不会对原对象进行修改。类似于 java 中的 String 对象
可变集合,就是这个集合可以直接对原对象进行修改,而不会返回新的对象。类似于 java 中 StringBuilder 对象
建议:在操作集合的时候,不可变用符号,可变用方法
for 循环有一个 1 to 3,就是 IndexedSeq 下的 Range
String 也是属于 IndexedSeq
IndexedSeq 是通过索引来查找和定位,因此速度快,比如 String 就是一个索引集合,通过索引即可定位
LinearSeq 是线型的,即有头尾的概念,这种数据结构一般是通过遍历来查找
第一种方式定义数组
定义:val arr1 = new Array [Int] (10)
new 是关键字
[Int]是指定可以存放的数据类型,如果希望存放任意数据类型,则指定 Any
(10),表示数组的大小,确定后就不可以变化
.:+ 运算符重载? :在前面 就是元素加在前面 :在后面就是加在后面
点号可以省略 空格隔开
package study06 class testArray { } object testArray{ def main(args: Array[String]): Unit = { // 1. 创建数组 val arr:Array[Int]=new Array[Int](5) // 另一种创建方式 val arr2=Array(12,23,43,53,54) // 2.访问元素 println(arr(0)) arr(0)=4231 println(arr(0)) println("=====================") // 3. 数组的遍历 // 3.1 普通for循环 for(i<-0 until arr.length){ println(arr(i)) } println("3.1 =====================") // 3.2 直接遍历所有元素 增强for循环 for(elem<-arr2) println(elem) println("3.2 =====================") // 3.3 迭代器 val iter=arr2.iterator while (iter.hasNext) println(iter.next()) println("3.3-------------------") // 3.4 调用foreach 方法 arr2.foreach((elem:Int)=>println(elem)) arr2.foreach(println) println(arr2.mkString(",")) println("3.4 =====================") // 4 添加元素 .:+ val newArr=arr2.:+(72) println(arr2.mkString("__")) println(newArr.mkString(",")) // 元素在前 对象在后 冒号 在对象前面 反之冒号在对象后面 val newArr4=19+:29+:newArr:+26:+777 println(newArr4.mkString(", ,")) } }
就记住一句话 :永远挨着已有集合的那边就可以了
第二种
val arr1 = Array(1, 2)
在定义数组时,直接赋初始值——使用 apply 方法创建数组对象
定义变长数组
val arr01 = ArrayBuffer [Any] (3, 2, 5)
ArrayBuffer 是有序的集合
增加元素使用的是 append 方法(),支持可变参数
[Any]存放任意数据类型
(3, 2, 5)初始化好的三个元素
ArrayBuffer 需要引入 scala.collection.mutable.ArrayBuffer
object TestArrayBuffer { def main(args: Array[String]): Unit = { // 创建并初始赋值可变数组 var arr01=ArrayBuffer[Any](1,2,3) // 遍历数组 for(i<-arr01){ println(i) } println("arr01.hash= "+arr01.hashCode()) // 增加元素 // 3.1 追加数据 arr01 += 4 arr01.+=(4) // 3.2 向数组最后追加数据 arr01.append(5,6) // 3.3 向指定的位置插入数据 0号位置 arr01.insert(0,7,8) println("arr01.hash= "+arr01.hashCode()) // 4 修改元素 arr01(1)=9 println("---------------------") for(i<-arr01){ println(i) } println(arr01.length) } }
arr1.toBuffer //不可变数组转可变数组
arr2.toArray //可变数组转不可变数组
多维数组定义
val arr = Array.ofDim [Double] (3,4)
说明:二维数组中有三个一维数组,每个一维数组中有四个元素
object DimArray {
def main(args: Array[String]): Unit = {
// 创建一个二维数组,有三个元素,每个元素是,含有4个元素一维数组()
val arr=Array.ofDim[Int](3,4)
// 2 遍历二维数组
for(i<-arr){ // i就是一维数组
for(j<-i){
print(j+" ")
}
println()
}
}
}
List 默认为不可变集合
集合间合并:将一个整体拆成一个一个的个体,称为扁平化
List子类 空集合对象 Nil
:: List 子类 extends List[B]
sealed 密封类
package study06 class testList { } object testList{ // 1 List 默认为不可变集合 // 2 创建一个List (数据有顺序,可重复) val list1:List[Int]=List(1,2,3,4,3) val list2=List(1,2,3,4,3) list1.foreach(println) // 3. 添加元素 val list3 = 10 +: list1 val list4 = list1 :+ 23 println(list4) println(list3) println("==================") //添加到第一个元素位置 val list5 = list2.::(51) println(list4) // 创建新列表 val list6 = Nil.::(13) println(list5) val list7 = 17 :: 28 :: 59 :: 16 :: Nil println(list7) // 4. 合并列表 val list8 = list6 :: list7 // 不能合并 println(list8) val list9 = list6 ::: list7 println(list9) val list10 = list6 ++ list7 println(list10) }
package chapter07 import scala.collection.mutable.ListBuffer object Test05_ListBuffer { def main(args: Array[String]): Unit = { // 1. 创建可变列表 val list1: ListBuffer[Int] = new ListBuffer[Int]() val list2 = ListBuffer(12, 53, 75) println(list1) println(list2) println("==============") // 2. 添加元素 list1.append(15, 62) list1.insert(1, 19, 22) list2.prepend(20) println(list1) println(list2) println("==============") 31 +=: 96 +=: list1 += 25 += 11 println(list1) println("list1 ==============") // 3. 合并list ++不改变list1 list2 val list3 = list1 ++ list2 println(list3) println("list3 ==============") // ++= 覆盖 list1 ++=: list2 println(list1) println(list2) println("==============") // 4. 修改元素 list2(3) = 30 list2.update(0, 89) println(list2) // 5. 删除元素 list2.remove(2) list2 -= 25 println(list2) } }
默认情况下,Scala 使用的是不可变集合,如果你想使用可变集合,需要引用scala.collection.mutable.Set 包
Set 默认是不可变集合,数据无序,数据不可重复
package chapter07 object Test06_ImmutableSet { def main(args: Array[String]): Unit = { // 1. 创建set val set1 = Set(13, 23, 53, 12, 13, 23, 78) println(set1) println("==================") // 2. 添加元素 val set2 = set1 + 129 println(set1) println(set2) println("==================") // 3. 合并set val set3 = Set(19, 13, 23, 53, 67, 99) val set4 = set2 ++ set3 println(set2) println(set3) println(set4) // 4. 删除元素 val set5 = set3 - 13 println(set3) println(set5) } }
object Test07_MutableSet { def main(args: Array[String]): Unit = { // 1. 创建set val set1: mutable.Set[Int] = mutable.Set(13, 23, 53, 12, 13, 23, 78) val s = mutable.Set(13, 23, 53, 12, 13, 23, 78) println(set1) println("==================") // 2. 添加元素 val set2 = set1 + 11 println(set1) println(set2) set1 += 11 println(set1) val flag1 = set1.add(10) // 返回 boolean true println(flag1) println(set1) val flag2 = set1.add(10) println(flag2) println(set1) println("==================") // 3. 删除元素 set1 -= 11 println(set1) val flag3 = set1.remove(10) // 返回 boolean true println(flag3) println(set1) val flag4 = set1.remove(10) println(flag4) println(set1) println("==================") // 4. 合并两个Set val set3 =set1.remove(10) } }
object Test08_ImmutableMap { def main(args: Array[String]): Unit = { // 1. 创建map val map1: Map[String, Int] = Map("a" -> 13, "b" -> 25, "hello" -> 3) val m1= Map("a" -> 13, "b" -> 25, "hello" -> 3) println(map1) println(map1.getClass) println("==========================") // 2. 遍历元素 map1.foreach(println) println("foreach ==========================") map1.foreach( (kv: (String, Int)) => println(kv) ) println("============================") // 3. 取map中所有的key 或者 value for (key <- map1.keys){ println(s"$key ---> ${map1.get(key)}") } // 4. 访问某一个key的value println("a: " + map1.get("a").get) println("c: " + map1.get("c")) println("c: " + map1.getOrElse("c", 0)) println(map1("a")) } }
package chapter07 import scala.collection.mutable object Test09_MutableMap { def main(args: Array[String]): Unit = { // 1. 创建map val map1: mutable.Map[String, Int] = mutable.Map("a" -> 13, "b" -> 25, "hello" -> 3) val m = mutable.Map("a" -> 13, "b" -> 25, "hello" -> 3) println(map1) println(map1.getClass) println("1==========================") // 2. 添加元素 map1.put("c", 5) map1.put("d", 9) println(map1) map1 += (("e", 7)) println(map1) println("2====================") // 3. 删除元素 println(map1("c")) map1.remove("c") println(map1.getOrElse("c", 0)) map1 -= "d" println(map1) println("3====================") // 4. 修改元素 map1.update("c", 5) map1.update("e", 10) println(map1) println("4====================") // 5. 合并两个Map val map2: Map[String, Int] = Map("aaa" -> 11, "b" -> 29, "hello" -> 5) // map1 ++= map2 println(map1) println(map2) println("---------------------------") val map3: Map[String, Int] = map2 ++ map1 val m3 = map2 ++ map1 println(map1) println(map2) println(map3) } }
元组也是可以理解为一个容器,可以存放各种相同或不同类型的数据。说的简单点,就是将多个无关的数据封装为一个整体,称为元组
注意:元组中最大只能有 22 个元素。
object Test10_Tuple { def main(args: Array[String]): Unit = { // 1. 创建元组 val tuple: (String, Int, Char, Boolean) = ("hello", 100, 'a', true) println(tuple) // 2. 访问数据 println(tuple._1) println(tuple._2) println(tuple._3) println(tuple._4) // 2.1 通过索引访问数据 println(tuple.productElement(1)) println("====================") // 3. 遍历元组数据 迭代器 for (elem <- tuple.productIterator) println(elem) // 3.1 Map 中的键值对其实就是元组,只不过元组的元素个数为 2,称之为 对偶 val map = Map("a"->1, "b"->2, "c"->3) val map1 = Map(("a",1), ("b",2), ("c",3)) println("====================") // 4. 嵌套元组 val mulTuple = (12, 0.3, "hello", (23, "scala"), 29) println(mulTuple._4._2) } }
object testCommonOp { private val list: List[Int] = List(1, 2, 3, 4, 5, 6) // 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(6)) }
package study06 object testCollectionDer { def main(args: Array[String]): Unit = { 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)) // 11 拉链 注:如果两个集合的元素个数不相等,那么会将同等数量的数据进行拉链,多余的数据省略不用 println("-----------------") println(list1.zip(list2)) // 12 滑窗 list1.sliding(2,5).foreach(println) } }
package chapter07 object Test13_SimpleFunction { def main(args: Array[String]): Unit = { val list = List(5,1,8,2,-3,4) val list2 = List(("a", 5), ("b", 1), ("c", 8), ("d", 2), ("e", -3), ("f", 4)) // (1)求和 var sum = 0 for (elem <- list){ sum += elem } println(sum) println(list.sum) // (2)求乘积 println(list.product) println("m========================") // (3)最大值 println(list.max) println(list2.maxBy( (tuple: (String, Int)) => tuple._2 )) println(list2.maxBy( _._2 )) // (4)最小值 println(list.min) println(list2.minBy(_._2)) println("========================") // (5)排序 // 5.1 sorted val sortedList = list.sorted println(sortedList) // 从大到小逆序排序 println(list.sorted.reverse) // 传入隐式参数 println(list.sorted(Ordering[Int].reverse)) // 按照 abcd 的顺序排序 元组的第一个元素 println(list2.sorted) // 5.2 sortBy 按照数字大小排序 println(list2.sortBy(_._2)) println(list2.sortBy(_._2)(Ordering[Int].reverse)) // 绝对值大小输出 并不改变原本元素 println("abs========================") println(list.sortBy(x => x.abs)) // 5.3 sortWith println(list.sortWith( (a: Int, b: Int) => {a < b} )) println(list.sortWith( _ < _ )) // 按元素降序排序 println(list.sortWith( _ > _)) } }
(1)sorted
对一个集合进行自然排序,通过传递隐式的 Ordering
(2)sortBy
对一个属性或多个属性进行排序,通过它的类型。
(3)sortWith
基于函数的排序,通过一个 comparator 函数,实现自定义排序的逻辑。
Map
过滤,转化/映射,扁平化,分组,简化(归约),折叠
package chapter07 object Test14_HighLevelFunction_Map { def main(args: Array[String]): Unit = { val list = List(1,2,3,4,5,6,7,8,9) // 1. 过滤 // 选取偶数 val evenList = list.filter( (elem: Int) => {elem % 2 == 0} ) println(evenList) // 选取奇数 println(list.filter( _ % 2 == 1 )) println("=======================") // 2. 映射map // 把集合中每个数乘2 println(list.map(_ * 2)) println(list.map( x => x * x)) println("=======================") // 3. 扁平化 flaten val nestedList: List[List[Int]] = List(List(1,2,3),List(4,5),List(6,7,8,9)) val flatList = nestedList(0) ::: nestedList(1) ::: nestedList(2) println(flatList) val flatList2 = nestedList.flatten println(flatList2) println("ff=======================") // 4. 扁平映射 // 将一组字符串进行分词,并保存成单词的列表 val strings: List[String] = List("hello world", "hello scala", "hello java", "we study") val splitList: List[Array[String]] = strings.map( _.split(" ") ) // 分词 val flattenList = splitList.flatten // 打散扁平化 println(flattenList) //flatMap 相当于先进行 map 操作,在进行 flatten 操作 //集合中的每个元素的子元素映射到某个函数并返回新集合 val flatmapList = strings.flatMap(_.split(" ")) println(flatmapList) println("========================") // 5. 分组groupBy // 分成奇偶两组 val groupMap: Map[Int, List[Int]] = list.groupBy( _ % 2) val groupMap2: Map[String, List[Int]] = list.groupBy( data => if (data % 2 == 0) "偶数" else "奇数") println(groupMap) println(groupMap2) // 给定一组词汇,按照单词的首字母进行分组 val wordList = List("china", "america", "alice", "canada", "cary", "bob", "japan") println( wordList.groupBy( _.charAt(0) ) ) } }
Reduce:通过指定的逻辑将集合中的数据进行聚合,从而减少数据,最终获取结果。
Fold
object Test15_HighLevelFunction_Reduce { def main(args: Array[String]): Unit = { val list = List(1,2,3,4) // 1. reduce println(list.reduce( _ + _ )) println(list.reduceLeft(_ + _)) println(list.reduceRight(_ + _)) // (1+(2+(3+4))) println("===========================") val list2 = List(3,4,5,8,10) println(list2.reduce(_ - _)) // -24=3-4-5-8-10 println(list2.reduceLeft(_ - _)) println(list2.reduceRight(_ - _)) // 3 - (4 - (5 - (8 - 10))), 6 println("===========================") // 2. fold // fold 方法使用了函数柯里化,存在两个参数列表 // 第一个参数列表为 : 零值(初始值) // 第二个参数列表为: 简化规则 // fold 底层其实为 foldLeft println(list.fold(10)(_ + _)) // 10 + 1 + 2 + 3 + 4 println(list.foldLeft(10)(_ - _)) // 10 - 1 - 2 - 3 - 4 println(list2.foldRight(11)(_ - _)) // 3 - (4 - (5 - (8 - (10 - 11)))), -5 // 标准写法 println(list2.fold(1)((x,y)=>x-y)) } }
map 合并案例
object Test16_MergeMap { def main(args: Array[String]): Unit = { val map1 = Map("a" -> 1, "b" -> 3, "c" -> 6) val map2 = mutable.Map("a" -> 6, "b" -> 2, "c" -> 9, "d" -> 3) // println(map1 ++ map2) 输出map2 val map3 = map1.foldLeft(map2)( (mergedMap, kv) => { val key = kv._1 val value = kv._2 mergedMap(key) = mergedMap.getOrElse(key, 0) + value mergedMap } ) println(map3) } }
单词计数:将集合中出现的相同的单词,进行计数,取计数排名前三的结果
object testWordCount { def main(args: Array[String]): Unit = { // 单词计数:将集合中出现的相同的单词,进行计数,取计数排名前三的结果 val stringList = List("Hello Scala Hbase kafka", "Hello Scala Hbase", "Hello Scala", "Hello") // 1) 将每一个字符串转换成一个一个单词 val wordList: List[String] = stringList.flatMap(str=>str.split(" ")) // 2) 将相同的单词放置在一起 val wordToWordsMap: Map[String, List[String]] = wordList.groupBy(word=>word) // 3) 对相同的单词进行计数 val wordCountMap: Map[String, Int] = wordToWordsMap.map(tuple => (tuple._1, tuple._2.size)) // 4) 对计数完成后的结果进行排序(降序) val sortList:List[(String,Int)]=wordCountMap.toList.sortWith{ (left,right)=>{ left._2>right._2 } } // 5) 对排序后的结果取前 3 名 val resultList: List[(String, Int)] = sortList.take(3) println(resultList) } }
方式一
package study06 object testWordCount02 { def main(args: Array[String]): Unit = { // 第一种方式(不通用) val tupleList = List(("Hello Scala Spark World ", 4), ("Hello Scala Spark", 3), ("Hello Scala", 2), ("Hello", 1)) val stringList: List[String] = tupleList.map(t=>(t._1 + " ") * t._2) //val words: List[String] = stringList.flatMap(s=>s.split(" ")) val words: List[String] = stringList.flatMap(_.split(" ")) // 在map中,如果传进来什么就返回什么,不要用_省略 //val groupMap: Map[String, List[String]] = words.groupBy(_) val groupMap:Map[String,List[String]]= words.groupBy(word=>word) val wordToCount:Map[String,Int]=groupMap.map(t=>(t._1,t._2.size)) val wordCountList:List[(String,Int)]=wordToCount.toList.sortWith{ (left,right)=>{ left._2>right._2 } }.take(3) //tupleList.map(t=>(t._1 + " ") * t._2).flatMap(_.split(" ")).groupBy(word=>word).map(t=>(t._1, t._2.size)) println(wordCountList) } }
方式二
object testWordCount03 { def main(args: Array[String]): Unit = { val tuples = List(("Hello Scala Spark World", 4), ("Hello Scala Spark", 3), ("Hello Scala", 2), ("Hello", 1)) // (Hello,4),(Scala,4),(Spark,4),(World,4) // (Hello,3),(Scala,3),(Spark,3) // (Hello,2),(Scala,2) // (Hello,1) val wordToCountList: List[(String, Int)] = tuples.flatMap { t => { val strings: Array[String] = t._1.split(" ") strings.map(word => (word, t._2)) } } // Hello, List((Hello,4), (Hello,3), (Hello,2), (Hello,1)) // Scala, List((Scala,4), (Scala,3), (Scala,2) // Spark, List((Spark,4), (Spark,3) // Word, List((Word,4)) val wordToTupleMap: Map[String, List[(String, Int)]] = wordToCountList.groupBy(t=>t._1) val stringToInts: Map[String, List[Int]] = wordToTupleMap.mapValues { datas => datas.map(t => t._2) } stringToInts /* val wordToCountMap: Map[String, List[Int]] = wordToTupleMap.map { t => { (t._1, t._2.map(t1 => t1._2)) } } val wordToTotalCountMap: Map[String, Int] = wordToCountMap.map(t=>(t._1, t._2.sum)) println(wordToTotalCountMap) */ } }
Scala 也提供了队列(Queue)的数据结构,队列的特点就是先进先出。进队和出队的方法分别为 enqueue 和 dequeue
object Test19_Queue { def main(args: Array[String]): Unit = { // 创建一个可变队列 val queue: mutable.Queue[String] = new mutable.Queue[String]() queue.enqueue("a", "b", "c") println(queue) println(queue.dequeue()) println(queue) println(queue.dequeue()) println(queue) queue.enqueue("d", "e") println(queue) println(queue.dequeue()) println(queue) println("==========================") // 不可变队列 val queue2: Queue[String] = Queue("a", "b", "c") val queue3 = queue2.enqueue("d") println(queue2) println(queue3) } }
Scala 为了充分使用多核 CPU,提供了并行集合(有别于前面的串行集合),用于多核环境的并行计算
object Test20_Parallel {
def main(args: Array[String]): Unit = {
val result: immutable.IndexedSeq[Long] = (1 to 100).map(
x => Thread.currentThread.getId
)
println(result)
// 并行集合
val result2: ParSeq[Long] = (1 to 100).par.map(
x => Thread.currentThread.getId
)
println(result2)
}
}
Vector(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)
ParVector(20, 20, 20, 31, 27, 27, 30, 30, 30, 41, 41, 41, 26, 26, 26, 42, 42, 42, 33, 33, 33, 35, 43, 42, 31, 22, 22, 22, 43, 25, 34, 35, 35, 35, 40, 40, 40, 27, 27, 27, 27, 27, 27, 34, 34, 34, 43, 43, 43, 43, 21, 21, 21, 32, 32, 32, 25, 25, 25, 31, 31, 31, 24, 24, 24, 38, 38, 38, 29, 29, 29, 39, 39, 41, 32, 23, 23, 23, 40, 30, 34, 36, 36, 36, 34, 34, 34, 28, 28, 28, 28, 28, 28, 37, 37, 37, 38, 32, 36, 39)
Scala 中的模式匹配类似于 Java 中的 switch 语法,但是 scala 从语法中补充了更多的功能,所以更加强大。
模式匹配语法中,采用 match 关键字声明,每个分支采用 case 关键字进行声明,当需要匹配时,会从第一个 case 分支开始,如果匹配成功,那么执行对应的逻辑代码,如果匹配不成功,继续执行下一个分支进行判断。如果所有 case 都不匹配,那么会执行 case _分支,类似于 Java 中default 语句
object Test01_PatternMatchBase { def main(args: Array[String]): Unit = { // 1. 基本定义语法 val x: Int = 5 val y: String = x match { case 1 => "one" case 2 => "two" case 3 => "three" case _ => "other" } println(y) // 2. 示例:用模式匹配实现简单二元运算 val a = 25 val b = 13 def matchDualOp(op: Char): Int = op match { case '+' => a + b case '-' => a - b case '*' => a * b case '/' => a / b case '%' => a % b case _ => -1 } println(matchDualOp('+')) println(matchDualOp('/')) println(matchDualOp('\\')) println("=========================") // 3. 模式守卫 // 求一个整数的绝对值 def abs(num: Int): Int = { num match { case i if i >= 0 => i case i if i < 0 => -i } } println(abs(67)) println(abs(0)) println(abs(-24)) } }
注意:
如果想要表达匹配某个范围的数据,就需要在模式匹配中增加条件守卫
object Test01_PatternMatchBase { def main(args: Array[String]): Unit = { // 3. 模式守卫 // 求一个整数的绝对值 def abs(num: Int): Int = { num match { case i if i >= 0 => i case i if i < 0 => -i } } println(abs(67)) println(abs(0)) println(abs(-24)) } }
Scala 中,模式匹配可以匹配所有的字面量,包括字符串,字符,数字,布尔值等等
object Test02_MatchTypes { def main(args: Array[String]): Unit = { // 1. 匹配常量 def describeConst(x: Any): String = x match { case 1 => "Int one" case "hello" => "String hello" case true => "Boolean true" case '+' => "Char +" case _ => "" } println(describeConst("hello")) println(describeConst('+')) println(describeConst(0.3)) } }
需要进行类型判断时,可以使用前文所学的 isInstanceOf[T]和 asInstanceOf[T],也可使用模式匹配实现同样的功能
object Test02_MatchTypes { def main(args: Array[String]): Unit = { // 2. 匹配类型 def describeType(x: Any): String = x match { case i: Int => "Int " + i case s: String => "String " + s case list: List[_] => "List " + list case array: Array[Int] => "Array[Int] " + array.mkString(",") case a => "Something else: " + a } println(describeType(35)) println(describeType("hello")) //泛型擦除 println(describeType(List("hi", "hello"))) println(describeType(List(2, 23))) //数组例外,可保留泛型 println(describeType(Array("hi", "hello"))) println(describeType(Array(2, 23))) } }
scala 模式匹配可以对集合进行精确的匹配,例如匹配只有两个元素的、且第一个元素为 0 的数组。
object Test02_MatchTypes { def main(args: Array[String]): Unit = { // 3. 匹配数组 for (arr <- List( Array(0), Array(1, 0), Array(0, 1, 0), Array(1, 1, 0), Array(2, 3, 7, 15), Array("hello", 1, 30), )) { val result = arr match { case Array(0) => "0" case Array(1, 0) => "Array(1, 0)" case Array(x, y) => "Array: " + x + ", " + y // 匹配两元素数组 case Array(0, _*) => "以0开头的数组" case Array(x, 1, z) => "中间为1的三元素数组" case _ => "something else" } println(result) } }
list 是一个存放 List 集合的数组
请思考,如果要匹配 List(88) 这样的只含有一个元素的列表,并原值返回.应该怎么写
方式一
object Test02_MatchTypes { def main(args: Array[String]): Unit = { for (list <- List( List(0), List(1, 0), List(0, 0, 0), List(1, 1, 0), List(88), List("hello") )) { val result = list match { case List(0) => "0" case List(x, y) => "List(x, y): " + x + ", " + y case List(0, _*) => "List(0, ...)" case List(a) => "List(a): " + a case _ => "something else" } println(result) } } }
方式二
object Test02_MatchTypes {
def main(args: Array[String]): Unit = {
val list1 = List(1, 2, 5, 7, 24)
val list = List(24)
list match {
case first :: second :: rest => println(s"first: $first, second: $second, rest: $rest")
case _ => println("something else")
}
}
}
object Test02_MatchTypes { def main(args: Array[String]): Unit = { for (tuple <- List( (0, 1), (0, 0), (0, 1, 0), (0, 1, 1), (1, 23, 56), ("hello", true, 0.5) )){ val result = tuple match { case (a, b) => "" + a + ", " + b case (0, _) => "(0, _)" case (a, 1, _) => "(a, 1, _) " + a case (x, y, z) => "(x, y, z) " + x + " " + y + " " + z case _ => "something else" } println(result) } }
扩展案例
object TestGeneric { def main(args: Array[String]): Unit = { //特殊的模式匹配 1 打印元组第一个元素 for (elem <- Array(("a", 1), ("b", 2), ("c", 3))) { println(elem._1) } for ((word,count) <- Array(("a", 1), ("b", 2), ("c", 3))) { println(word) } for ((word,_) <- Array(("a", 1), ("b", 2), ("c", 3))) { println(word) } for (("a",count) <- Array(("a", 1), ("b", 2), ("c", 3))) { println(count) } println("--------------") //特殊的模式匹配 2 给元组元素命名 var (id,name,age): (Int, String, Int) = (100, "zs", 20) println((id,name,age)) println("--------------") //特殊的模式匹配 3 遍历集合中的元组,给 count * 2 var list: List[(String, Int)] = List(("a", 1), ("b", 2), ("c", 3)) //println(list.map(t => (t._1, t._2 * 2))) println( list.map{ case (word,count)=>(word,count*2) } ) var list1 = List(("a", ("a", 1)), ("b", ("b", 2)), ("c", ("c", 3))) println( list1.map{ case (groupkey,(word,count))=>(word,count*2) } ) } }
val user = User(“zhangsan”,11),该语句在执行时,实际调用的是 User 伴生对象中的apply 方法,因此不用 new 关键字就能构造出相应的对象。
当将 **User(“zhangsan”, 11)**写在 case 后时[case User(“zhangsan”, 11) => “yes”],会默认调用 unapply 方法(对象提取器),user 作为 unapply 方法的参数,unapply 方法将 user 对象的 name 和 age 属性提取出来,与 User(“zhangsan”, 11)中的属性值进行匹配
case 中对象的 unapply 方法(提取器)返回 Some,且所有属性均一致,才算匹配成功,属性不一致,或返回 None,则匹配失败。
若只提取对象的一个属性,则提取器为 unapply(obj:Obj): Option[T]
若提取对象的多个属性,则提取器为 unapply(obj:Obj): Option[(T1,T2,T3…)]
若提取对象的可变个属性,则提取器为 unapplySeq(obj:Obj): Option[Seq[T]
package chapter08 object Test04_MatchObject { def main(args: Array[String]): Unit = { val student = new Student("alice", 18) // 针对对象实例的内容进行匹配 val result = student match { case Student("alice", 18) => "Alice, 18" case _ => "Else" } println(result) } } // 定义类 class Student(val name: String, val age: Int) // 定义伴生对象 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)) } } }
语法:case class Person (name: String, age: Int)
样例类仍然是类,和普通类相比,只是其自动生成了伴生对象,并且伴生对象中自动提供了一些常用的方法,如 apply、unapply、toString、equals、hashCode 和 copy。
样例类是为模式匹配而优化的类,因为其默认提供了 unapply 方法,因此,样例类可以直接使用模式匹配,而无需自己实现 unapply 方法。
构造器中的每一个参数都成为 val,除非它被显式地声明为 var(不建议这样做)
object Test05_MatchCaseClass { def main(args: Array[String]): Unit = { val student = Student1("alice", 18) // 针对对象实例的内容进行匹配 val result = student match { case Student1("alice", 18) => "Alice, 18" case _ => "Else" } println(result) } } // 定义样例类 case class Student1(name: String, age: Int)
偏函数也是函数的一种,通过偏函数我们可以方便的对输入参数做更精确的检查。例如该偏函数的输入类型为 List[Int],而我们需要的是第一个元素是 0 的集合,这就是通过模式匹配实现的
注:该偏函数的功能是返回输入的 List 集合的第二个元素
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YzT07Zq8-1659264210160)(E:/%E5%AD%A6%E4%B9%A0/%E5%A4%A7%E6%95%B0%E6%8D%AE/scala/image-20220720230805653.png)]
上述代码会被 scala 编译器翻译成以下代码,与普通函数相比,只是多了一个用于参数检查的函数——isDefinedAt,其返回值类型为 Boolean。
val second = new PartialFunction[List[Int], Option[Int]] {
//检查输入参数是否合格
override def isDefinedAt(list: List[Int]): Boolean = list match
{
case x :: y :: _ => true
case _ => false
}
//执行函数逻辑
override def apply(list: List[Int]): Option[Int] = list match
{
case x :: y :: _ => Some(y)
} }
偏函数使用
偏函数不能像 second(List(1,2,3))这样直接使用,因为这样会直接调用 apply 方法,而应
该调用 applyOrElse 方法,如下
second.applyOrElse(List(1,2,3), (_: List[Int]) => None)
applyOrElse 方法的逻辑为 if (ifDefinedAt(list)) apply(list) else default。如果输入参数满
足条件,即 isDefinedAt 返回 true,则执行 apply 方法,否则执行 defalut 方法,default 方法
为参数不满足要求的处理逻辑
package chapter08 object Test06_PartialFunction { def main(args: Array[String]): Unit = { val list: List[(String, Int)] = List(("a", 12), ("b", 35), ("c", 27), ("a", 13)) // 1. map转换,实现key不变,value2倍 val newList = list.map( tuple => (tuple._1, tuple._2 * 2) ) // 2. 用模式匹配对元组元素赋值,实现功能 val newList2 = list.map( tuple => { tuple match { case (word, count) => (word, count * 2) } } ) // 3. 省略lambda表达式的写法,进行简化 val newList3 = list.map { case (word, count) => (word, count * 2) } println(newList) println(newList2) println(newList3) // 偏函数的应用,求绝对值 // 对输入数据分为不同的情形:正、负、0 val positiveAbs: PartialFunction[Int, Int] = { case x if x > 0 => x } val negativeAbs: PartialFunction[Int, Int] = { case x if x < 0 => -x } val zeroAbs: PartialFunction[Int, Int] = { case 0 => 0 } def abs(x: Int): Int = (positiveAbs orElse negativeAbs orElse zeroAbs) (x) println(abs(-67)) println(abs(35)) println(abs(0)) } }
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 块中。在 try 块之后使用了一个 catch 处理程序来捕获异常。如果发生任何异常,catch 处理程序将处理它,程序将不会异常终止。
Scala 的异常的工作机制和 Java 一样,但是 Scala 没有“checked(编译期)”异常, 即 Scala 没有编译异常这个概念,异常都是在运行的时候捕获处理。
异常捕捉的机制与其他语言中一样,如果有异常发生,catch 子句是按次序捕捉的。因此,在 catch 子句中,越具体的异常越要靠前,越普遍的异常越靠后,如果把越普遍的异常写在前,把具体的异常写在后,在 Scala 中也不会报错,但这样是非常不好的编程风格。
finally 子句用于执行不管是正常处理还是有异常发生时都需要执行的步骤,一般用于对象的清理工作,这点和 Java 一样。
用 throw 关键字,抛出一个异常对象。所有异常都是 Throwable 的子类型。throw 表达式是有类型的,就是 Nothing,因为 Nothing 是所有类型的子类型,所以 throw 表达式可以用在需要类型的地方
def test():Nothing = {
throw new Exception("不对")
}
java 提供了 throws 关键字来声明异常。可以使用方法定义声明异常。它向调用者函数提供了此方法可能引发此异常的信息。它有助于调用函数处理并将该代码包含在 try-catch块中,以避免程序异常终止。在 Scala 中,可以使用 throws 注解来声明异常
def main(args: Array[String]): Unit = {
f11()
}
@throws(classOf[NumberFormatException])
def f11()={
"abc".toInt
}
当编译器第一次编译失败的时候,会在当前的环境中查找能让代码编译通过的方法,用于将类型进行转换,实现二次编译
使用 implicit 关键字声明的函数称之为隐式函数
隐式转换可以在不需改任何代码的情况下,扩展某个类的功能。
需求:通过隐式转化为 Int 类型增加方法
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 Test02_Implicit { def main(args: Array[String]): Unit = { // 1. 隐式函数 implicit def convert(num: Int): MyRichInt = new MyRichInt(num) println(12.myMax(15)) } }
普通方法或者函数中的参数可以通过 implicit 关键字声明为隐式参数,调用该方法时,就可以传入该参数,编译器会在相应的作用域寻找符合条件的隐式值。
(1)同一个作用域中,相同类型的隐式值只能有一个
(2)编译器按照隐式参数的类型去寻找对应类型的隐式值,与隐式值的名称无关。
(3)隐式参数优先于默认参数
object Test02_Implicit { def main(args: Array[String]): Unit = { // 3. 隐式参数 implicit val str: String = "alice" // implicit val str2: String = "alice2" implicit val num: Int = 18 def sayHello()(implicit name: String): Unit = { println("hello, " + name) } def sayHi(implicit name: String = "atguigu"): Unit = { println("hi, " + name) } sayHello sayHi // 简便写法 def hiAge(): Unit = { println("hi, " + implicitly[Int]) } hiAge() } }
在 Scala2.10 后提供了隐式类,可以使用 implicit 声明类,隐式类的非常强大,同样可以扩展类的功能,在集合中隐式类会发挥重要的作用。
(1)其所带的构造参数有且只能有一个
(2)隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类不能是顶级的。
object Test02_Implicit {
def main(args: Array[String]): Unit = {
// 2. 隐式类
implicit class MyRichInt2(val self: Int) {
// 自定义比较大小的方法
def myMax2(n: Int): Int = if ( n < self ) self else n
def myMin2(n: Int): Int = if ( n < self ) n else self
}
println(12.myMin2(15))
}
}
//(2)如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。 类型的作用域是指与该类型相关联的全部伴生模块, object TestTransform extends PersonTrait { def main(args: Array[String]): Unit = { //(1)首先会在当前代码作用域下查找隐式实体 val teacher = new Teacher() teacher.eat() teacher.say() } class Teacher { def eat(): Unit = { println("eat...") } } } trait PersonTrait { } object PersonTrait { // 隐式类 : 类型 1 => 类型 2 implicit class Person5(user:Teacher) { def say(): Unit = { println("say...") } } }
class MyList[+T]{ //协变
}
class MyList[-T]{ //逆变
}
class MyList[T] //不变
说明
协变:Son 是 Father 的子类,则 MyList[Son] 也作为 MyList[Father]的“子类”。
逆变:Son 是 Father 的子类,则 MyList[Son]作为 MyList[Father]的“父类”。
不变:Son 是 Father 的子类,则 MyList[Father]与 MyList[Son]“无父子关系”。
// 定义继承关系 class Parent {} class Child extends Parent {} class SubChild extends Child {} // 定义带泛型的集合类型 -E 逆变 class MyCollection[-E] {} object Test03_Generics { def main(args: Array[String]): Unit = { // 1. 协变和逆变 val child: Parent = new Child // val childList: MyCollection[Parent] = new MyCollection[Child] val childList: MyCollection[SubChild] = new MyCollection[Child] // 2. 上下限 def test[A <: Child](a: A): Unit = { println(a.getClass.getName) } test[SubChild](new SubChild) } }
泛型的上下限的作用是对传入的泛型进行限定。
Class PersonList[T <: Person]{ //泛型上限}
Class PersonList[T >: Person]{ //泛型下限}
class Parent{} class Child extends Parent{} class SubChild extends Child{} object Scala_TestGeneric { 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) } }
语法
def f [ A : B ] (a: A) = println(a) //等同于 def f [ A ] (a:A)(implicit arg:B[A])=println(a)
说明
上下文限定是将泛型和隐式转换的结合产物,以下两者功能相同,使用上下文限定[A : Ordering]之后,方法内无法使用隐式参数名调用隐式参数,需要通过 implicitly[Ordering[A]]
获取隐式变量,如果此时无法查找到对应类型的隐式变量,会发生出错误
implicit val x = 1
val y = implicitly[Int]
val z = implicitly[Double]
def f[A:Ordering](a:A,b:A) =implicitly[Ordering[A]].compare(a,b)
def f[A](a: A, b: A)(implicit ord: Ordering[A]) = ord.compare(a, b)
ctrl+X 删除行
ctrl+H 显示类的结构图(类的继承层次)
ctrl+enter 导入包,自动修正
ctrl+R 替换文本
ctrl+F 查找文本
ctrl+space 代码提示
ctrl+shift+space 自动补全代码
ctrl+alt+T 可以把代码包在一块内,例如: try/catch
ctrl+alt+V 可以引入变量。例如:new String();自动导入变量定义
ctrl+alt+I 将选中的代码进行自动缩进编排,这个功能在编辑jsp文件时也可以规则
ctrl+alt+O 优化导入的类和包
("hello, " + name)
}
def sayHi(implicit name: String = “atguigu”): Unit = {
println("hi, " + name)
}
sayHello
sayHi
// 简便写法
def hiAge(): Unit = {
println("hi, " + implicitly[Int])
}
hiAge()
}
}
### 隐式类 在 Scala2.10 后提供了隐式类,可以使用 **implicit** 声明类,隐式类的非常强大,同样可以扩展类的功能,在集合中隐式类会发挥重要的作用。 (1)其所带的构造参数有且只能有一个 (2)**隐式类必须被定义在“类”或“伴生对象”或“包对象”里**,即隐式类不能是**顶级的**。 ```scala object Test02_Implicit { def main(args: Array[String]): Unit = { // 2. 隐式类 implicit class MyRichInt2(val self: Int) { // 自定义比较大小的方法 def myMax2(n: Int): Int = if ( n < self ) self else n def myMin2(n: Int): Int = if ( n < self ) n else self } println(12.myMin2(15)) } }
//(2)如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。 类型的作用域是指与该类型相关联的全部伴生模块, object TestTransform extends PersonTrait { def main(args: Array[String]): Unit = { //(1)首先会在当前代码作用域下查找隐式实体 val teacher = new Teacher() teacher.eat() teacher.say() } class Teacher { def eat(): Unit = { println("eat...") } } } trait PersonTrait { } object PersonTrait { // 隐式类 : 类型 1 => 类型 2 implicit class Person5(user:Teacher) { def say(): Unit = { println("say...") } } }
class MyList[+T]{ //协变
}
class MyList[-T]{ //逆变
}
class MyList[T] //不变
说明
协变:Son 是 Father 的子类,则 MyList[Son] 也作为 MyList[Father]的“子类”。
逆变:Son 是 Father 的子类,则 MyList[Son]作为 MyList[Father]的“父类”。
不变:Son 是 Father 的子类,则 MyList[Father]与 MyList[Son]“无父子关系”。
// 定义继承关系 class Parent {} class Child extends Parent {} class SubChild extends Child {} // 定义带泛型的集合类型 -E 逆变 class MyCollection[-E] {} object Test03_Generics { def main(args: Array[String]): Unit = { // 1. 协变和逆变 val child: Parent = new Child // val childList: MyCollection[Parent] = new MyCollection[Child] val childList: MyCollection[SubChild] = new MyCollection[Child] // 2. 上下限 def test[A <: Child](a: A): Unit = { println(a.getClass.getName) } test[SubChild](new SubChild) } }
泛型的上下限的作用是对传入的泛型进行限定。
Class PersonList[T <: Person]{ //泛型上限}
Class PersonList[T >: Person]{ //泛型下限}
class Parent{} class Child extends Parent{} class SubChild extends Child{} object Scala_TestGeneric { 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) } }
语法
def f [ A : B ] (a: A) = println(a) //等同于 def f [ A ] (a:A)(implicit arg:B[A])=println(a)
说明
上下文限定是将泛型和隐式转换的结合产物,以下两者功能相同,使用上下文限定[A : Ordering]之后,方法内无法使用隐式参数名调用隐式参数,需要通过 implicitly[Ordering[A]]
获取隐式变量,如果此时无法查找到对应类型的隐式变量,会发生出错误
implicit val x = 1
val y = implicitly[Int]
val z = implicitly[Double]
def f[A:Ordering](a:A,b:A) =implicitly[Ordering[A]].compare(a,b)
def f[A](a: A, b: A)(implicit ord: Ordering[A]) = ord.compare(a, b)
ctrl+X 删除行
ctrl+H 显示类的结构图(类的继承层次)
ctrl+enter 导入包,自动修正
ctrl+R 替换文本
ctrl+F 查找文本
ctrl+space 代码提示
ctrl+shift+space 自动补全代码
ctrl+alt+T 可以把代码包在一块内,例如: try/catch
ctrl+alt+V 可以引入变量。例如:new String();自动导入变量定义
ctrl+alt+I 将选中的代码进行自动缩进编排,这个功能在编辑jsp文件时也可以规则
ctrl+alt+O 优化导入的类和包
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。