广州公司网站,阜宁建设网站的公司,微信官网客户端,东莞网站建设环保设备Scala2.12
视频地址
1 入门
1.1 发展历史
…
1.2 Scala 和 Java Scala Java 编写代码使用scalac编译成.class字节码文件scala .class文件 执行代码 1.3 特点 1.4 安装
视频地址
注意配置好环境变量 简单代码 1.5 编译文件 编译scala文件会产生两个.class文件 使用java…Scala2.12
视频地址
1 入门
1.1 发展历史
…
1.2 Scala 和 Java Scala Java 编写代码使用scalac编译成.class字节码文件scala .class文件 执行代码 1.3 特点 1.4 安装
视频地址
注意配置好环境变量 简单代码 1.5 编译文件 编译scala文件会产生两个.class文件 使用java命令执行scala编译出的class文件报错 正确方法如下 1.6 IDEA使用 创建maven项目 安装scala插件 创建java 和scala目录同时设置成源代码目录 右击项目名 - 添加框架支持 1.7 HelloWorld 创建包和类 输入main即可自动补全 基本格式 1.8 class 和 object static关键字 调用方法:类名.属性 // 在java中 static修饰的字段是属于类的也就是所有创建的对象都会有这个属性
public class Student {private String name;private Integer age;private static String school atguigu;public Student(String name, Integer age) {this.name name;this.age age;}public void printInfo(){System.out.println(this.name this.age Student.school);}public static void main(String[] args) {Student alice new Student(alice, 20);Student bob new Student(bob, 23);alice.printInfo();bob.printInfo();}
}而在scala中使用伴生对象代替了static字段也就是类存在伴生对象就存在 class Student(name: String, age: Int) {def printInfo(): Unit {println(name age Student.school)}
}// 引入伴生对象
object Student{val school: String atguigudef main(args: Array[String]): Unit {val alice new Student(alice, 20)val bob new Student(bob, 23)alice.printInfo()bob.printInfo()}
}1.9 反编译 2 变量和数据类型
2.1 注释
和java完全一样
2.2 变量和常量 原则 声明变量时类型可以省略编译器自动推导即类型推导var a1 10类型确定后就不能修改说明Scala是强数据类型语言变量声明时必须要有初始值在声明/定义一个变量时可以使用var或者val来修饰var修饰的变量可改变val修饰的变量不可改。
2.3 命名规范
基本与Java一致
以字母或者下划线开头后接字母、数字、下划线以操作符开头且只包含操作符 - * / # !等用反引号…包括的任意字符串即使是 Scala 关键字39 个也可以 关键字 2.4 字符串输出 基本用法 字符串通过连接printf 用法字符串通过%传值。字符串模板插值字符串通过$获取变量值
var name: String jinlianvar age: Int 18//1字符串通过号连接println(name age)
//2printf 用法字符串通过%传值。printf(name%s age%d\n, name, age)
//3字符串通过$引用
/*多行字符串在 Scala中利用三个双引号包围多行字符串就可以实现。输入的内容带有空格、\t 之类导致每一行的开始位置不能整洁对齐。应用 scala 的 stripMargin 方法在 scala 中 stripMargin 默认是“|”作为连接符在多行换行的行头前面加一个“|”符号即可。
*/
val s |select|name,|age|from user|where namezhangsan.stripMargin
println(s)//如果需要对变量进行运算那么可以加${}
val s1 s|select| name,| age|from user|where name$name and age${age2}.stripMarginprintln(s1)val s2 sname$nameprintln(s2) 2.5 键盘输入 基本用法 StdIn.readLine() StdIn.readShort() StdIn.readDouble() // 1 输入姓名println(input name:)var name StdIn.readLine()// 2 输入年龄println(input age:)var age StdIn.readShort()// 3 输入薪水println(input sal:)var sal StdIn.readDouble()// 4 打印println(name name)println(age age)println(sal sal)2.6 数字类型 Java scala 2.7 整数类型 Byte、Short、Int、Long 定义不要超过类型的范围 // 正确var n1:Byte 127var n2:Byte -128// 错误// var n3:Byte 128// var n4:Byte -129Scala 的整型默认为 Int 型声明 Long 型须后加‘l’或‘L’ var n5 10
println(n5)
var n6 9223372036854775807L
println(n6)Scala 程序中变量常声明为 Int 型除非不足以表示大数才使用 Long 2.8 浮点类型 Scala 的浮点类型可以表示一个小数比如 123.4f7.80.12 等等。 Scala 的浮点型常量默认为 Double 型声明 Float 型常量须后加‘f’或‘F’。 // 建议在开发中需要高精度小数时请选择 Doublevar n7 2.2345678912fvar n8 2.23456789122.9 字符类型 字符类型可以表示单个字符字符类型是 Char。 字符常量是用单引号 ’ ’ 括起来的单个字符。\t 一个制表位实现对齐的功能\n 换行符\\ 表示\\ 表示 虽然IDEA会报错 但是默认会自动进行强制类型转换 这样写就会报错 2.10 Boolean类型
同Java 2.11 Unit 、Null 、 Nothing Unit Null 类只有一个实例对象Null 类似于 Java 中的 null 引用。Null 可以赋值给任 意引用类型AnyRef但是不能赋值给值类型AnyVal object TestDataType {def main(args: Array[String]): Unit {//null 可以赋值给任意引用类型AnyRef但是不能赋值给值类型AnyValvar cat new Cat();cat null // 正确var n1: Int null // 错误println(n1: n1)}
}
class Cat {
} Nothing可以作为没有正常返回值的方法的返回类型非常直观的告诉你这个方 法不会正常返回而且由于 Nothing 是其他任意类型的子类他还能跟要求返回值的方法兼容。 def main(args: Array[String]): Unit {def test() : Nothing{throw new Exception()}test()}2.12 类型转换 基本同Java object TestValueTransfer {def main(args: Array[String]): Unit {//1自动提升原则有多种类型的数据混合运算时系统首先自动将所有
数据转换成精度大的那种数值类型然后再进行计算。var n 1 2.0println(n) // n 就是 Double//2把精度大的数值类型赋值给精度小的数值类型时就会报错反之就
会进行自动类型转换。var n2 : Double 1.0//var n3 : Int n2 //错误原因不能把高精度的数据直接赋值和低
精度。//3byteshort和 char 之间不会相互自动转换。var n4 : Byte 1//var c1 : Char n4 //错误var n5:Int n4//4byteshortchar 他们三者可以计算在计算时首先转换为 int
类型。var n6 : Byte 1var c2 : Char 1// var n : Short n6 c2 //当 n6 c2 结果类型就是 int// var n7 : Short 10 90 //错误}
}强制类型转换 Java : int num (int)2.5
Scala : var num : Int 2.7.toIntvar 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数值类型与String类型转换 基本类型转 String 类型语法将基本类型的值“” 即可String 类型转基本数值类型语法s1.toInt、s1.toFloat、s1.toDouble、s1.toByte、s1.toLong、s1.toShort 例 def main(args: Array[String]): Unit {var n: Int 130var b: Byte n.toByteprintln(b) //-126 原因Byte最大值为127,-128 -127 -126
}3 运算符 大体同Java 3.1 注意事项 Scala更加类似于 Java 中的 equals def main(args: Array[String]): Unit {val s1 abcval s2 new String(abc)println(s1 s2)
println(s1.eq(s2))
}
输出结果
true
falseScala 中没有、--操作符可以通过、-来实现同样的效果 3.2 运算符本质 在 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 1println(1.toString())println(1 toString())println(1 toString)}
}4 流程控制 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(age18 age30){中年}else{老年}println(res)}
}Java 中的三元运算符可以用 if else 实现 如果大括号{}内的逻辑代码只有一行大括号可以省略。如果省略大括号if 只对最近 的一行逻辑代码起作用。 object TestIfElse {def main(args: Array[String]): Unit {// Java
// int result flag?1:0// Scalaprintln(input age)var age StdIn.readInt()val res:Any if (age 18) 童年 else 成年println(res)}
}
4.1 Switch 分支结构
在 Scala 中没有 Switch而是使用模式匹配来处理。
4.2 For循环控制 基本用法 to // i 将会从 1-3 循环前后闭合
for(i - 1 to 3){print(i )
}
i 1 2 3Until // 前闭合后开
for(i - 1 until 3) {print(i )
}
i 1 24.3 循环守卫
for(i - 1 to 3 if i ! 2) {print(i )
}
println()
// 等价于
for (i - 1 to 3){
if (i ! 2) {print(i )
}
}
4.4 循环步长
// by 表示步长
for (i - 1 to 10 by 2) {println(i i)
}
i 1 3 5 7 94.5 嵌套循环
// 没有关键字所以范围后一定要加来隔断逻辑
for(i - 1 to 3; j - 1 to 3) {println( i i j j)
}
// 等价于
for (i - 1 to 3) {for (j - 1 to 3) {println(i i j j)}
}4.6 引入变量
for(i - 1 to 3; j 4 - i) {println(i i j j)
}for {i - 1 to 3j 4 - i
} {println(i i j j)
}for (i - 1 to 3) {var j 4 - iprintln(i i j j)
}
// 三者等价4.7 循环返回值 yield 类似于js中的map // 将遍历过程中处理的结果返回到一个新 Vector 集合中
val res for(i - 1 to 10) yield i
println(res)
// 结果res Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)// 将原数据中所有值乘以 2并把数据返回到一个新的集合中
var res for(i -1 to 10) yield {i * 2}
// 结果res Vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)4.8 倒序打印 reverse
for(i - 1 to 10 reverse){println(i)
}4.9 While循环控制
与 for 语句不同while 语句没有返回值即整个 while 语句的结果是 Unit 类型()因为 while 中没有返回值所以当要用该语句来计算并返回结果时就不可避免的使用变量而变量需要声明在 while 循环的外部那么就等同于循环的内部对外部的变量 造成了影响所以不推荐使用而是推荐使用 for 循环。 显然while循环不能满足大数据并行处理的要求因为他们公用的都是外部的变量比如控制循环的i def main(args: Array[String]): Unit {var i 0//变量必须在while外面声明while (i 10) {println(宋宋喜欢海狗人参丸 i)i 1}
}4.10 循环中断 Scala 内置控制结构特地去掉了 break 和 continue是为了更好的适应函数式编程推荐使用函数式的风格解决break和continue的功能而不是一个关键字。Scala中使用breakable 控制结构来实现 break 和 continue 功能。 采用异常的方式退出循环 def main(args: Array[String]): Unit {try {for (elem - 1 to 10) {println(elem)if (elem 5) throw new RuntimeException}} catch {case e }println(正常结束循环)
}采用 Scala 自带的函数退出循环 import scala.util.control.Breaks
def main(args: Array[String]): Unit {Breaks.breakable(for (elem - 1 to 10) {println(elem)if (elem 5) Breaks.break()})println(正常结束循环)
}对 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(正常结束循环)}
}5 函数式编程 面向对象编程 解决问题分解对象行为属性然后通过对象的关系以及行为的调用来解决问题。 对象用户 行为登录、连接 JDBC、读取数据库 属性用户名、密码 Scala 语言是一个完全面向对象编程语言。 万物皆对象 对象的本质对数据和行为的一个封装 函数式编程 解决问题时将问题分解成一个一个的步骤将每个步骤进行封装函数通过调用这些封装好的步骤解决问题。 例如请求-用户名、密码-连接 JDBC-读取数据库
Scala 语言是一个完全函数式编程语言。万物皆函数。
函数的本质函数可以当做一个值进行传递
5.1 函数基础
5.1.1 函数基本语法 // 定义一个函数实现将传入的名称打印出来。
object TestFunction {
def main(args: Array[String]): Unit {// 1函数定义def f(arg: String): Unit {println(arg)}// 2函数调用// 函数名参数f(hello world)}
}
5.1.2 函数和方法的区别 概念 为完成某一功能的程序语句的集合称为函数。类中的函数称之方法。 案例实操 Scala 语言可以在任何的语法结构中声明任何的语法函数没有重载和重写的概念方法可以进行重载和重写Scala 中函数可以嵌套定义
// (2)方法可以进行重载和重写程序可以执行
def main(): Unit {
}def main(args: Array[String]): Unit {
// 1Scala 语言可以在任何的语法结构中声明任何的语法
import java.util.Date
new Date()// (2)函数没有重载和重写的概念程序报错
def test(): Unit {println(无参无返回值)
}
test()
def test(name: String): Unit {println()
}//3Scala 中函数可以嵌套定义
def test2(): Unit {def test3(name: String): Unit {println(函数可以嵌套定义)}
}
}5.1.3 函数定义 def main(args: Array[String]): Unit {// 函数 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)}
}5.1.4 参数函数
可变参数如果参数列表中存在多个参数那么可变参数一般放置在最后参数默认值一般将有默认值的参数放置在参数列表的后面带名参数
object TestFunction {def main(args: Array[String]): Unit {// 1可变参数def test( s : String* ): Unit {println(s)}// 有输入参数输出 Arraytest(Hello, Scala)// 无输入参数输出 List()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(nameximenqing)}
}
5.1.5 函数至简原则
能省就省
return 可以省略Scala 会使用函数体的最后一行代码作为返回值如果函数体只有一行代码可以省略花括号返回值类型如果能够推断出来那么可以省略:和返回值类型一起省略如果有 return则不能省略返回值类型必须指定如果函数明确声明 unit那么即使函数体中使用 return 关键字也不起作用Scala 如果期望是无返回值类型可以省略等号如果函数无参但是声明了参数列表那么调用时小括号可加可不加如果函数没有参数列表那么小括号可以省略调用时小括号必须省略如果不关心名称只关心逻辑处理那么函数名def可以省略
object TestFunction {def main(args: Array[String]): Unit {// 0函数标准写法def f( s : String ): String {return s jinlian}println(f(Hello))// 至简原则:能省则省//1 return 可以省略,Scala 会使用函数体的最后一行代码作为返回值def f1( s : String ): String {s jinlian}println(f1(Hello))//2如果函数体只有一行代码可以省略花括号def f2(s:String):String s jinlian//3返回值类型如果能够推断出来那么可以省略:和返回值类型一起省略def f3( s : String ) s jinlianprintln(f3(Hello3))//4如果有 return则不能省略返回值类型必须指定。def f4() :String {return ximenqing4}println(f4())//5如果函数明确声明 unit那么即使函数体中使用 return 关键字也不起作用def f5(): Unit {return dalang5}println(f5())//6一般不使用 Scala 如果期望是无返回值类型,可以省略等号// 将无返回值的函数称之为过程def f6() {dalang6}println(f6()) //输出值为(),因为没有等号 是无返回值类型//7如果函数无参但是声明了参数列表那么调用时小括号可加可不加def f7() dalang7println(f7())println(f7)//8如果函数没有参数列表那么小括号可以省略,调用时小括号必须省略def f8 dalang//println(f8())println(f8)//9如果不关心名称只关心逻辑处理那么函数名def可以省略//这就是匿名函数 lambda表达式(x:String){println(wusong)}}5.2 函数高级
5.2.1 高阶函数 函数作为参数输入 _的用法 (3) f中传入一个函数函数的参数为name方法体为输出name
(4) 可以知道f中传入的函数就是将一个参数输出无论参数的名字叫什么所以可以用_代替参数名 // 2. 函数作为参数进行传递// 定义二元计算函数def dualEval(op: (Int, Int)Int, a: Int, b: Int): Int {op(a, b)}def add(a: Int, b: Int): Int {a b}println(dualEval(add, 12, 35))println(dualEval((a, b) a b, 12, 35))println(dualEval(_ _, 12, 35))函数作为值进行传递即一个函数等于另外一个函数f2 f _表示f2这个函数赋值为f 函数作为函数的返回值返回 // 函数f5的参数为空它的返回值为一个输入参数为Int返回值为空的函数
def f5(): IntUnit {def f6(a: Int): Unit {println(f6调用 a)}f6 // 将函数直接返回
}
// 调用函数,相当于f5()返回的是一个函数名后面的(25)是参数
f5()(25)5.2.2 匿名函数
(x:Int){函数体}至简原则 参数的类型可以省略会根据形参进行自动的推导 类型省略之后发现只有一个参数则圆括号可以省略其他情况没有参数和参数超过 1 的永远不能省略圆括号。 匿名函数如果只有一行则大括号也可以省略 如果参数只出现一次则参数省略且后面参数可以用_代替 如果执行的是一步操作那么可以直接省略_ 例 简化 5.2.3 实战 对数组进行操作定义运算函数 def main(args: Array[String]): Unit {val arr: Array[Int] Array(12, 45, 75, 98)// 对数组进行处理将操作抽象出来处理完毕之后的结果返回一个新的数组def arrayOperation(array: Array[Int], op: IntInt): Array[Int] {for (elem - array) yield op(elem)}// 定义一个加一操作def addOne(elem: Int): Int {elem 1}// 调用函数val newArray: Array[Int] arrayOperation(arr, addOne)// 将数组中的元素取出来 并用分隔println(newArray.mkString(,))// 传入匿名函数实现元素翻倍val newArray2 arrayOperation(arr, _ * 2)// 将数组中的元素取出来 并用分隔println(newArray2.mkString(, ))}定义一个匿名函数并将它作为值赋给变量 fun。函数有三个参数类型分别为 IntStringChar返回值类型为 Boolean。要求调用函数 fun(0, “”, ‘0’)得到返回值为 false其它情况均返回 true。 val fun (i: Int, s: String, c: Char) {if (i 0 s c 0) false else true}println(fun(0, , 0))println(fun(0, , 1))println(fun(23, , 0))println(fun(0, hello, 0))println()定义一个函数 func它接收一个 Int 类型的参数返回一个函数记作 f1。 它返回的函数 f1接收一个 String 类型的参数同样返回一个函数记作 f2。函数 f2 接 收一个 Char 类型的参数返回一个 Boolean 的值。 要求调用函数 func(0) (“”) (‘0’)得到返回值为 false其它情况均返回 true。 def func(i: Int): String(CharBoolean) {def f1(s: String): CharBoolean {def f2(c: Char): Boolean {if (i 0 s c 0) false else true}f2}f1}println(func(0)()(0))println(func(0)()(1))println(func(23)()(0))println(func(0)(hello)(0))// 匿名函数简写// 匿名函数接收String类型的参数s,返回值为一个函数// 匿名函数接收Char类型的参数c返回值为Booleandef func1(i: Int): String (Char Boolean) {s {c {if (i 0 s c 0) false else true}}}
// 省去括号就变成下面的样子def func1(i: Int): String(CharBoolean) {s c if (i 0 s c 0) false else true}println(func1(0)()(0))println(func1(0)()(1))println(func1(23)()(0))println(func1(0)(hello)(0))// 柯里化def func2(i: Int)(s: String)(c: Char): Boolean {if (i 0 s c 0) false else true}println(func2(0)()(0))println(func2(0)()(1))println(func2(23)()(0))println(func2(0)(hello)(0))}5.2.4 函数柯里化和闭包 闭包如果一个函数访问到了它的外部局部变量的值那么这个函数和他所处的环境称为闭包 原理 scala是面向对象的所有的方法都有一个地址即在堆中保存了下来所以即使先执行的方法出栈了但其数据任然保存在堆中后面执行的方法可以访问到数据 object TestFunction {def main(args: Array[String]): Unit {def f1(){var a:Int 10def 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))5.2.5 递归
同Java
5.2.6 控制抽象 值调用参数传值,平常的传值方法 def foo(a: Int):Unit {println(a) }名调用传名参数把代码块传递过去有返回值 def f1(): Int {println(f1调用)12}
// 2. 传名参数传递的不再是具体的值而是代码块def f2(a: Int): Unit {println(a: a)println(a: a)}// 传递的是f1这个代码块相当于f2中的参数af1()f2(f1())f2({println(这是一个代码块)29})案例自定义while循环 使用柯里化最容易理解while(代码块)(代码块)
package chapter05object Test12_MyWhile {def main(args: Array[String]): Unit {var n 10// 1. 常规的while循环while (n 1){println(n)n - 1}// 2. 用闭包实现一个函数将代码块作为参数传入递归调用def myWhile(condition: Boolean): (Unit)Unit {// 内层函数需要递归调用参数就是循环体def doLoop(op: Unit): Unit {if (condition){opmyWhile(condition)(op)}}doLoop _}println()n 10myWhile(n 1){println(n)n - 1}// 3. 用匿名函数实现def myWhile2(condition: Boolean): (Unit)Unit {// 内层函数需要递归调用参数就是循环体op {if (condition){opmyWhile2(condition)(op)}}}println()n 10myWhile2(n 1){println(n)n - 1}// 3. 用柯里化实现最容易理解def myWhile3(condition: Boolean)(op: Unit): Unit {if (condition){opmyWhile3(condition)(op)}}println()n 10myWhile3(n 1){println(n)n - 1}}
}
5.2.7 惰性加载 当函数返回值被声明为 lazy 时函数的执行将被推迟直到我们首次对此取值该函 数才会执行。这种函数我们称之为惰性函数。 def main(args: Array[String]): Unit {lazy val res sum(10, 30)println(----------------)println(res res)
}
def sum(n1: Int, n2: Int): Int {println(sum 被执行。。。)return n1 n2
}6 面向对象
Scala 的面向对象思想和 Java 的面向对象思想和概念是一致的。
Scala 中语法和 Java 不同补充了更多的功能。
6.1 Scala包
6.1.1 包的命名
同java
6.1.2 包语句 有两种风格 com.jaken.scala嵌套风格
package com{package jaken{package scala{}}
}优点 一个源文件中可以声明多个 package子包中的类可以直接访问父包中的内容而无需导包
package com {import com.atguigu.Inner //父包访问子包需要导包object Outer {val out: String outdef main(args: Array[String]): Unit {println(Inner.in)}}package atguigu {object Inner {val in: String indef main(args: Array[String]): Unit {println(Outer.out) //子包访问父包无需导包}}}}package other {
}6.1.3 包对象
在 Scala 中可以为每个包定义一个同名的包对象(package object)定义在包对象中的成员作为其对应包下所有 class 和 object 的共享变量可以被直接访问。
package object com{val shareValuesharedef shareMethod(){}
}package com {object Outer {val out: String outdef main(args: Array[String]): Unit {// 可以直接拿到包对象println(shareValue)}}
}6.1.4 导包说明
同java
6.2 类和对象
类可以看成一个模板
对象表示具体的事物
6.2.1 定义类和属性
Scala 中没有 public一个.scala 中可以写多个类。
Scala 语法中类并不声明为 public所有这些类都具有公有可见性即默认就是 public一个 Scala 源文件可以包含多个类
class Person {var name: String bobo //定义属性var age: Int _ // _表示给属性一个默认值,Int默认为0String为null//Bean 属性BeanProperty修饰的属性有getter/setterBeanProperty var sex: String 男}
object Person {def main(args: Array[String]): Unit {var person new Person()println(person.name)person.setSex(女)println(person.getSex)}
}6.3 封装
Scala 中的 public 属性底层实际为 private并通过 get 方法obj.field()和 set 方法 obj.field_(value)对其进行操作。
所以 Scala 并不推荐将属性设为 private再为其设置 public 的 get 和 set 方法的做法。但由于很多 Java 框架都利用反射调用 getXXX 和 setXXX 方 法有时候为了和这些框架兼容也会为 Scala 的属性设置 getXXX 和 setXXX 方法通过 BeanProperty 注解实现。
6.3.1 访问权限
Scala 中属性和方法的默认访问权限为 public但 Scala 中无 public 关键字。private 为私有权限只在类的内部和伴生对象中可用。protected 为受保护权限Scala 中受保护权限比 Java 中更严格同类、子类可以访问同包无法访问。private[包名]增加包访问权限包名下的其他类也可以使用 例 在子类中idCard由于是私有属性 所以不能访问 在实例对象中protect对象不能访问 6.3.2 构造方法
object Test05_Constructor {def main(args: Array[String]): Unit {// 调用的主构造方法val student1 new Student1//调用的一般方法student1.Student1()//调用的辅助构造方法1val student2 new Student1(alice)//调用的辅助构造方法2val student3 new Student1(bob, 25)}
}// 定义一个类,其实也是主构造方法
class Student1() {// 定义属性var name: String _var age: Int _println(1. 主构造方法被调用)// 声明辅助构造方法1def this(name: String) {// 首先必须调用主构造器this() println(2. 辅助构造方法一被调用)this.name nameprintln(sname: $name age: $age)}// 声明辅助构造方法2def this(name: String, age: Int){//调用构造方法1this(name)println(3. 辅助构造方法二被调用)this.age ageprintln(sname: $name age: $age)}
// 不同于JAVA这并不是一个构造方法而是一个普通的方法def Student1(): Unit {println(一般方法被调用)}
}运行结果 6.3.3构造方法参数 实操 object Test06_ConstructorParams {def main(args: Array[String]): Unit {val student2 new Student2student2.name alicestudent2.age 18println(sstudent2: name ${student2.name}, age ${student2.age})val student3 new Student3(bob, 20)println(sstudent3: name ${student3.name}, age ${student3.age})val student4 new Student4(cary, 25)//由于Student4的参数未修饰所以参数是局部变量而不是成员属性
// println(sstudent4: name ${student4.name}, age ${student4.age})student4.printInfo()val student5 new Student5(bob, 20)println(sstudent3: name ${student5.name}, age ${student5.age})student3.age 21val student6 new Student6(cary, 25, atguigu)println(sstudent6: name ${student6.name}, age ${student6.age})student6.printInfo()}
}// 定义类
// 无参构造器
class Student2 {// 单独定义属性var name: String _var age: Int _
}// 上面定义等价于
class Student3(var name: String, var age: Int)// 主构造器参数无修饰,name和age属性就相当于局部变量而不是成员属性
class Student4(name: String, age: Int){def printInfo(){println(sstudent4: name ${name}, age $age)}
}//class Student4(_name: String, _age: Int){
// var name: String _name
// var age: Int _age
//}
//用常量修饰
class Student5(val name: String, val age: Int)
//用变量修饰
class Student6(var name: String, var age: Int){var school: String _def this(name: String, age: Int, school: String){this(name, age)this.school school}def printInfo(){println(sstudent6: name ${name}, age $age, school $school)}
}运行结果 6.4 继承 object Test07_Inherit {def main(args: Array[String]): Unit {val student1: Student7 new Student7(alice, 18)val student2 new Student7(bob, 20, std001)student1.printInfo()student2.printInfo()val teacher new Teacherteacher.printInfo()def personInfo(person: Person7): Unit {person.printInfo()}println()val person new Person7personInfo(student1)personInfo(teacher)personInfo(person)}
}// 定义一个父类
class Person7() {var name: String _var age: Int _println(1. 父类的主构造器调用)def this(name: String, age: Int){this()println(2. 父类的辅助构造器调用)this.name namethis.age age}def printInfo(): Unit {println(sPerson: $name $age)}
}// 定义子类
class Student7(name: String, age: Int) extends Person7(name, age) {var stdNo: String _println(3. 子类的主构造器调用)def this(name: String, age: Int, stdNo: String){this(name, age)println(4. 子类的辅助构造器调用)this.stdNo stdNo}override def printInfo(): Unit {println(sStudent: $name $age $stdNo)}
}class Teacher extends Person7 {override def printInfo(): Unit {println(sTeacher)}
}6.5 多态(动态绑定) 与java的区别 scala中的属性和方法都是动态绑定也就是说new的是什么就调用什么的属性和方法
java中的属性是静态绑定方法是动态绑定也就是说定义为父类而new子类调用的属性是父类的属性值调用的方法是子类的方法 java多态 public class TestDynamicBind {public static void main(String[] args) {Worker worker new Worker();System.out.println(worker.name);worker.hello();worker.hi();System.out.println();// 多态定义的是Person父类但new的是Worder子类Person person new Worker();// 静态绑定属性所以打印的是person的属性System.out.println(person.name); // 动态绑定方法所以打印的是worker的方法person.hello(); // 由于父类中没有hi方法所以报错
// person.hi(); // error}
}class Person {String name person;public void hello() {System.out.println(hello person);}
}class Worker extends Person {String name worker;public void hello() {System.out.println(hello worker);}public void hi() {System.out.println(hi worker);}
}scala多态 object Test08_DynamicBind {def main(args: Array[String]): Unit {//同样定义的是父类实例化的是子类val student: Person8 new Student8//与JAVA不同SCALA的属性也是动态绑定所以是Student8的属性println(student.name)//调用的是是Student8的方法student.hello()}
}class Person8 {val name: String persondef hello(): Unit {println(hello person)}
}class Student8 extends Person8 {override val name: String studentoverride def hello(): Unit {println(hello student)}
}6.6 抽象类 基本语法 定义抽象类abstract class Person{} //通过 abstract 关键字标记抽象类定义抽象属性val|var name:String //一个属性没有初始化就是抽象属性定义抽象方法def hello():String //只声明而没有实现的方法就是抽象方法
package chapter06object Test09_AbstractClass {def main(args: Array[String]): Unit {val student new Student9student.eat()student.sleep()}
}// 定义抽象类
abstract class Person9{// 非抽象属性var name: String person// 抽象属性var age: Int// 非抽象方法def eat(): Unit {println(person eat)}// 抽象方法def sleep(): Unit
}// 定义具体的实现子类
class Student9 extends Person9 {// 实现抽象属性必须varoverride可有可无var age: Int 18// 实现抽象方法直接定义就行override可有可无def sleep(): Unit {println(student sleep)}// 重写非抽象属性因为父类name是var修饰的直接写就可以// 如果父类是val,则子类都不可以修改直接继承父类的
// override val name: String studentname student//重写非抽象方法override def eat(): Unit {super.eat()println(student eat)}
}6.6.1 匿名抽象类
object Test10_AnnoymousClass {def main(args: Array[String]): Unit {val person: Person10 new Person10 {//有无override都可override var name: String aliceoverride def eat(): Unit println(person eat)}println(person.name)person.eat()}
}// 定义抽象类
abstract class Person10 {var name: Stringdef eat(): Unit
}6.7 单例对象(伴生对象)
Scala语言是完全面向对象的语言所以并没有静态的操作即在Scala中没有静态的概念。但是为了能够和Java语言交互因为Java中有静态概念就产生了一种特殊的对象来模拟类对象该对象为单例对象。若单例对象名与类名一致则称该单例对象这个类的伴生对象这个类的所有“静态”内容都可以放置在它的伴生对象中声明。 基本语法 object Test11_Object {def main(args: Array[String]): Unit {
// val student new Student11(alice, 18)
// student.printInfo()// 实现构造方法私有化也就是类.“静态”方法val student1 Student11.newStudent(alice, 18)student1.printInfo()val student2 Student11.apply(bob, 19)student2.printInfo()val student3 Student11(bob, 19)student3.printInfo()}
}// 定义类private表明主构造器私有化了
class Student11 private(val name: String, val age: Int){def printInfo(): Unit {// 注意这里的Student11.school也就是类名.属性println(sstudent: name ${name}, age $age, school ${Student11.school})}
}// 伴生对象可以访问伴生类的私有成员和方法
object Student11{//这个变量 就相当于java中的static修饰的静态变量val school: String atguigu// 定义一个类的对象实例的创建方法该方法也是静态方法def newStudent(name: String, age: Int): Student11 new Student11(name, age)//如果参数名称是apply则调用的时候可以省略类.apply(),直接写Student11(bob, 19)def apply(name: String, age: Int): Student11 new Student11(name, age)
}
6.7.1 单例设计模式 也就是类只有一个类中的属性先前就定义好了写死了 object Test12_Singleton {def main(args: Array[String]): Unit {val student1 Student12.getInstance()student1.printInfo()val student2 Student12.getInstance()student2.printInfo()// student1和student2的地址是相同的println(student1)println(student2)}
}class Student12 private(val name: String, val age: Int){def printInfo(): Unit {println(sstudent: name ${name}, age $age, school ${Student11.school})}
}// 饿汉式无论Student12类是否存在都会重新new
//object Student12 {
// private val student: Student12 new Student12(alice, 18)
// def getInstance(): Student12 student
//}// 懒汉式如果单例类不存在才会new
object Student12 {// 相当于定义了静态变量student,它是Student12类型的只有一份private var student: Student12 _def getInstance(): Student12 {if (student null){// 如果没有对象实例的话就创建一个student new Student12(alice, 18)}student}
}6.8 特质(trait)
某个类它的本质就用它的父类来体现是继承关系。比如student类的本质就用它的父类person来体现。 motivation 然而像student类他会有young person或是old person这两个类的特性是不一样的在java中就定义了接口使类能够保持它的特性
scala中没有接口的概念采用特质 trait特征来代替。Scala 中的 trait 中即可以有抽象属性和方法也可以有具体的属性和方法一个类可以混入mixin多个特质。这种感觉类似于 Java 中的抽象类。 Scala 引入 trait 特征第一可以替代 Java 的接口第二个也是对单继承机制的一种补充有点类似于多继承的味道但实际上还是为单继承。 基本语法 没有父类class 类名 extends 特质 1 with 特质 2 with 特质 3
有父类class 类名 extends 父类 with 特质 1 with 特质 2 with 特质 3
package chapter06object Test13_Trait {def main(args: Array[String]): Unit {val student: Student13 new Student13student.sayHello()student.study()student.dating()student.play()}
}// 定义一个父类
class Person13 {val name: String personvar age: Int 18def sayHello(): Unit {println(hello from: name)}def increase(): Unit {println(person increase)}
}// 定义一个特质
trait Young {// 声明抽象和非抽象属性var age: Intval name: String young// 声明抽象和非抽象的方法def play(): Unit {println(syoung people $name is playing)}def dating(): Unit
}class Student13 extends Person13 with Young {// Person13中有nameperson,Yong中有nameyoung,需要重写冲突的属性override val name: String student// 实现抽象方法def dating(): Unit println(sstudent $name is dating)def study(): Unit println(sstudent $name is studying)// 重写父类方法override def sayHello(): Unit {super.sayHello()println(shello from: student $name)}
}6.8.1 多特质(mixin)动态混入
package chapter06object Test14_TraitMixin {def main(args: Array[String]): Unit {val student new Student14student.study()student.increase()student.play()student.increase()student.dating()student.increase()println()// 动态混入,也就是要什么特性就给什么特质val studentWithTalent new Student14 with Talent {override def dancing(): Unit println(student is good at dancing)override def singing(): Unit println(student is good at singing)}studentWithTalent.sayHello()studentWithTalent.play()studentWithTalent.study()studentWithTalent.dating()studentWithTalent.dancing()studentWithTalent.singing()}
}// 再定义一个特质
trait Knowledge {var amount: Int 0def increase(): Unit
}trait Talent {def singing(): Unitdef dancing(): Unit
}class Student14 extends Person13 with Young with Knowledge {// 重写冲突的属性override val name: String student// 实现抽象方法def dating(): Unit println(sstudent $name is dating)def study(): Unit println(sstudent $name is studying)// 重写父类方法override def sayHello(): Unit {super.sayHello()println(shello from: student $name)}// 实现特质中的抽象方法override def increase(): Unit {amount 1println(sstudent $name knowledge increased: $amount)}
}6.8.2 特质的叠加 由于一个类可以混入mixin多个 trait且 trait 中可以有具体的属性和方法若混入的特质中具有相同的方法方法名参数列表返回值均相同必然会出现继承冲突问题。 冲突分为以下两种 解决这类冲突问题直接在类Sub中重写冲突方法。 所谓的“钻石问题”解决这类冲突问题Scala 采用了特质叠加的策略。 package chapter06object Test15_TraitOverlying {def main(args: Array[String]): Unit {// 钻石问题特征叠加val myFootBall new MyFootBallprintln(myFootBall.describe())}
}
// 定义球类特征
trait Ball {def describe(): String ball
}
// 定义颜色特征
trait ColorBall extends Ball {var color: String redoverride def describe(): String color - super.describe()
}// 定义种类特征
trait CategoryBall extends Ball {var category: String footoverride def describe(): String category - super.describe()
}// 定义一个自定义球的类
class MyFootBall extends CategoryBall with ColorBall {override def describe(): String my ball is a super[CategoryBall].describe()
} 案例中的 super不是表示其父特质对象而是表示上述叠加顺序中的下一个特质 即MyClass 中的 super 指代 ColorColor 中的 super 指代 CategoryCategory 中的 super 指代 Ball。 6.8.3 指定super
除了上面的推断外也可以指定使用哪个父类的方法 6.8.4 特质和抽象类的区别
优先使用特质。一个类扩展多个特质是很方便的但却只能扩展一个抽象类。如果你需要构造函数参数使用抽象类。因为抽象类可以定义带参数的构造函数 而特质不行有无参构造。
6.8.5 特质自身类型
自身类型可实现依赖注入的功能。
object Test16_TraitSelfType {def main(args: Array[String]): Unit {val user new RegisterUser(alice, 123456)user.insert()}
}// 用户类
class User(val name: String, val password: String)trait UserDao {// 相当于依赖注入的效果 _只是一个通配符 表示UserDao会用到User,但它们没有继承关系_: User // 向数据库插入数据def insert(): Unit {// 使用this来调用Userprintln(sinsert into db: ${this.name})}
}// 定义注册用户类
class RegisterUser(name: String, password: String) extends User(name, password) with UserDao6.9 扩展
6.9.1 类型检查和转换 obj.isInstanceOf[T]判断 obj 是不是 T 类型。 obj.asInstanceOf[T]将 obj 强转成 T 类型。 classOf 获取对象的类名。
6.9.2 枚举类和应用类
// 定义枚举类对象
object WorkDay extends Enumeration {// 1,2表示键也就是底层的存储val MONDAY Value(1, Monday)val TUESDAY Value(2, TuesDay)
}// 定义应用类对象
object TestApp extends App {println(app start)// type 起别名type MyString Stringval a: MyString abcprintln(a)
}// 2. 测试枚举类直接引用就行
println(WorkDay.MONDAY)7 集合
Scala 的集合有三大类序列 Seq、集 Set、映射 Map所有的集合都扩展自 Iterable 特质。对于几乎所有的集合类Scala 都同时提供了可变和不可变的版本分别位于以下两 个包(scala.collection.immutable 、scala.collection.mutable)Scala 不可变集合就是指该集合对象不可修改每次修改就会返回一个新对象而 不会对原对象进行修改。类似于 java 中的 String 对象可变集合就是这个集合可以直接对原对象进行修改而不会返回新的对象。类似 于 java 中 StringBuilder 对象 建议 在操作集合的时候不可变用符号可变用方法
7.1 不可变集合继承图 7.2 可变集合继承图 7.3 数组 相当于JAVA中的List
7.3.1 不可变数组
并不是说数组的值不可以修改而是指向该数组的地址是不变的 创建数组 val arr1 new Array[Int](10)
// 这里调用的是Array.apply()方法
val arr2 Array(1,54,46,15,45)数组遍历和访问 // 数组访问
println(arr(0))
// 3. 数组的遍历
// 1) 普通for循环 until前闭后开
for (i - 0 until arr.length){println(arr(i))
}
for (i - arr.indices) println(arr(i))
println(---------------------)
// 2) 直接遍历所有元素增强for循环
for (elem - arr2) println(elem)
println(---------------------)
// 3) 迭代器
val iter arr2.iterator
while (iter.hasNext)println(iter.next())
println(---------------------)
// 4) 调用foreach方法
arr2.foreach( (elem: Int) println(elem) )
arr.foreach( println )
println(arr2.mkString(--)) //打印结果为12--37--42--58--97数组添加 // 4. 添加元素在数组后面加用:
val newArr arr2.:(73)
// arr2是不变的
println(arr2.mkString(--))
println(newArr.mkString(--))
// 在数组前面添加元素用:
val newArr2 newArr.:(30)
println(newArr2.mkString(--))
//所有的运算符也是函数可以省略()
val newArr3 newArr2 : 15
// 一定在数字那边
val newArr4 19 : 29 : newArr3 : 26 : 73
println(newArr4.mkString(, ))7.3.2 可变数组 创建数组 val arr1: ArrayBuffer[Int] new ArrayBuffer[Int]()
val arr2 ArrayBuffer(23, 57, 92)
//直接使用println可以输出
println(arr2)数组遍历和访问 // 遍历和访问同不可变数组
println(arr2(1))添加元素 // 3. 添加元素
val newArr1 arr1 : 15
// arr1依旧是不变的
println(arr1)
println(newArr1)
println(arr1 newArr1)
// 对于可变数组而言非常不推荐将返回的值再赋给另外一个变量
val newArr2 arr1 19
// arr1 改变了
println(arr1)
println(newArr2)
// 是一个东西 结果为true
println(arr1 newArr2)
// 如果newArr2改变了
newArr2 13
// arr1 也会改变
println(arr1)
// 在数组前面添加元素
77 : arr1
println(arr1)
println(newArr2)
// 在后面添加可变数组推荐使用方法
arr1.append(36)
// 在前面添加
arr1.prepend(11, 76)
// 在索引为1的位置添加13和39
arr1.insert(1, 13, 59)
println(arr1)
// 在索引为2的位置添加一个数组
arr1.insertAll(2, newArr1)
// 在前面添加一个数组
arr1.prependAll(newArr2)
// 在后面添加一个数组
arr1.appendAll(newArr2)删除元素 // 4. 删除索引3的元素
arr1.remove(3)
println(arr1)
// 从索引0开始删除10个数
arr1.remove(0, 10)
println(arr1)
// 删除第一个值为13的数如果13不在数组内啥也不做
arr1 - 137.3.3 可变数组和不可变数组的转换
// 5. 可变数组转换为不可变数组toArray
val arr: ArrayBuffer[Int] ArrayBuffer(23, 56, 98)
val newArr: Array[Int] arr.toArray
println(newArr.mkString(, ))
println(arr)// 6. 不可变数组转换为可变数组toBuffer
val buffer: mutable.Buffer[Int] newArr.toBuffer
println(buffer)
println(newArr)7.3.4 多维数组
// 1. 创建二维数组
val array: Array[Array[Int]] Array.ofDim[Int](2, 3)// 2. 访问元素
array(0)(2) 19
array(1)(0) 25
// 3.遍历二维数组
println(array.mkString(, ))
for (i - 0 until array.length; j - 0 until array(i).length){println(array(i)(j))
}
for (i - array.indices; j - array(i).indices){print(array(i)(j) \t)if (j array(i).length - 1) println()
}
array.foreach(line line.foreach(println))
// 简化写法
array.foreach(_.foreach(println))7.4 列表 相当于JAVA中的LinkedList
7.4.1 不可变列表 创建list // 1. 创建一个List不能new只能使用伴生对象的.apply方法
val list1 List(23, 65, 87)
println(list1)
// List(73,32)
val list6 73 :: 32 :: Nil
// List(17,28,59,6 )
val list7 17 :: 28 :: 59 :: 16 :: Nil访问和遍历list // 2. 访问和遍历元素
println(list1(1))
// 无法修改元素的值 list1(1) 12
list1.foreach(println)添加元素 val list2 10 : list1
val list3 list1 : 23
println(list1)
println(list2)
println(list3)
println()
// 调用特殊的方法::将51放在list的前面
val list4 list2.::(51)
println(list4)
// 一般用Nil.::(元素)在创建新list
val list5 Nil.::(13)
println(list5)
// List(73,32)
val list6 73 :: 32 :: Nil
// List(17,28,59,16)
val list7 17 :: 28 :: 59 :: 16 :: Nil
println(list7)合并列表 // 4. 合并列表
// List(73,32)
val list6 73 :: 32 :: Nil
// List(17,28,59,16)
val list7 17 :: 28 :: 59 :: 16 :: Nil
val list8 list6 :: list7
// list8List(List(73,32),17,28,59,16)
println(list8)
// list9List(73,32,17,28,59,16)
val list9 list6 ::: list7
println(list9)
// 结果同上
val list10 list6 list7
println(list10)7.4.2可变列表
// 1. 创建可变列表
val list1: ListBuffer[Int] new ListBuffer[Int]()
val list2 ListBuffer(12, 53, 75)
println(list1)
println(list2)
println()// 2. 添加元素
list1.append(15, 62)
list2.prepend(20)
// 在索引为1的位置加19,22
list1.insert(1, 19, 22)
println(list1)
println(list2)println()
31 : 96 : list1 25 11
println(list1)
println()
// 3. 合并list
val list3 list1 list2
// list1 list2并不更改
println(list1)
println(list2)println()
// list2改变因为有:是从右到左改变
list1 : list2
println(list1)
println(list2)println()// 4. 修改索引为3的元素
list2(3) 30
// 修改索引为0的元素为89
list2.update(0, 89)
println(list2)// 5. 删除元素
list2.remove(2)
// 指定删除值为25的值
list2 - 25
println(list2)7.5 集合Set
7.5.1 不可变集合
// 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)7.5.2 可变集合
// 1. 创建set,使用mutable.Set
val set1: mutable.Set[Int] mutable.Set(13, 23, 53, 12, 13, 23, 78)
println(set1)
println()// 2. 添加元素
val set2 set1 11
// set1并没有改变
println(set1)
println(set2)
// set1改变了
set1 11
println(set1)
// 修改了set就返回true
val flag1 set1.add(10)
println(flag1)
println(set1)
val flag2 set1.add(10)
println(flag2)
println(set1)
println()// 3. 删除元素
set1 - 11
println(set1)
val flag3 set1.remove(10)
println(flag3)
println(set1)
val flag4 set1.remove(10)
println(flag4)
println(set1)
println()// 4. 合并两个Set,set1改变set2不变
set1 set27.6 Map
7.6.1 不可变Map
// 1. 创建map
val map1: Map[String, Int] Map(a - 13, b - 25, hello - 3)
println(map1)
println(map1.getClass) //class scala.collection.immutable.Map$Map3
println()
// 2. 遍历元素
map1.foreach(println)
map1.foreach( (kv: (String, Int)) println(kv) )
println()
// 3. 取map中所有的key 或者 value
for (key - map1.keys){println(s$key --- ${map1.get(key)}) //输出为a --- Some(13)
}
// 4. 访问某一个key的value
println(a: map1.get(a).get) //获得具体的值map1.get(a).get)
// 获得key为a的值
println(map1(a))
println(c: map1.get(c))
// 如果c不存在返回0
println(c: map1.getOrElse(c, 0))7.6.2 可变Map
// 1. 创建map
val map1: mutable.Map[String, Int] mutable.Map(a - 13, b - 25, hello - 3)
println(map1)
println(map1.getClass) // class scala.collection.mutable.HashMap
println()// 2. 添加元素
map1.put(c, 5)
map1.put(d, 9)
println(map1)
// 注意是使用(()),原因是若只加一个(),编译器会以为省略了一个(),中间的内容又是另外一个函数也就是两个函数
map1 ((e, 7))
println(map1)
println()// 3. 删除元素
println(map1(c))
map1.remove(c)
println(map1.getOrElse(c, 0))
map1 - d
println(map1)
println()// 4. 修改元素
map1.update(c, 5)
map1.update(e, 10)
println(map1)
println()// 5. 合并两个Mapmap2是不可变的
val map2: Map[String, Int] Map(aaa - 11, b - 29, hello - 5)
// map1修改
map1 map2
println(map1)
println(map2)
println(---------------------------)
val map3: Map[String, Int] map2 map1
println(map1)
println(map2)
println(map3)7.7 元组
元组也是可以理解为一个容器可以存放各种相同或不同类型的数据。元组中最大只能有 22 个元素。
// 1. 创建元组
val tuple: (String, Int, Char, Boolean) (hello, 100, a, true)
println(tuple)// 2. 访问数据,从下标1开始
println(tuple._1)
println(tuple._2)
println(tuple._3)
println(tuple._4)println(tuple.productElement(1))println()
// 3. 遍历元组数据
for (elem - tuple.productIterator)println(elem)// 4. 嵌套元组
val mulTuple (12, 0.3, hello, (23, scala), 29)
println(mulTuple._4._2)7.8 集合函数
7.8.1 常用函数
val list List(1,3,5,7,2,89)
val set Set(23,34,423,75)// 1获取集合长度,只有线性序列才有的属性
println(list.length)// 2获取集合大小 set不能获取长度
println(set.size)// 3循环遍历
for (elem - list)println(elem)set.foreach(println)// 4迭代器
for (elem - list.iterator) println(elem)println()
// 5生成字符串
println(list)
println(set)
println(list.mkString(--))// 6是否包含
println(list.contains(23))
println(set.contains(23))7.8.2 衍生操作
val list1 List(1,3,5,7,2,89)
val list2 List(3,7,2,45,4,8,19)// 1获取集合的头1
println(list1.head)// 2获取集合的尾不是头的就是尾List(3, 5, 7, 2, 89)
println(list1.tail)// 3集合最后一个数据19
println(list2.last)// 4集合初始数据不包含最后一个List(3, 7, 2, 45, 4, 8)
println(list2.init)// 5反转List(89, 2, 7, 5, 3, 1)
println(list1.reverse)// 6取前后n个元素
println(list1.take(3))
println(list1.takeRight(4))// 7去掉前后n个元素
println(list1.drop(3))
println(list1.dropRight(4))println()
// 8并集
val union list1.union(list2)
println(union: union)
println(list1 ::: list2)// 如果是set做并集会去重
val set1 Set(1,3,5,7,2,89)
val set2 Set(3,7,2,45,4,8,19)val union2 set1.union(set2)
println(union2: union2)
println(set1 set2)
println(-----------------------)// 9交集
val intersection list1.intersect(list2)
println(intersection: intersection)
println(-----------------------)// 10差集存在一个集合中(list1)但不存在另外一个集合中(list2)
val diff1 list1.diff(list2)
val diff2 list2.diff(list1)
println(diff1: diff1)
println(diff2: diff2)
println(-----------------------)// 11拉链(a,b,c) (d,e,f,g) ((a,d),(b,e),(c,f))
println(zip: list1.zip(list2))//List((1,3), (3,7), (5,2), (7,45), (2,4), (89,8))
println(zip: list2.zip(list1))//List((3,1), (7,3), (2,5), (45,7), (4,2), (8,89))
println(-----------------------)// 12滑窗一定划过整个数组
// list1(1,3,5,7,2,89) (1, 3, 5) (3, 5, 7) (5, 7, 2) (7, 2, 89)
for (elem - list1.sliding(3))println(elem)
println(-----------------------)
// 窗口大小为4每次滑动2个
for (elem - list2.sliding(4, 2))println(elem)println(-----------------------)
for (elem - list2.sliding(3, 3))println(elem)7.8.3 数学操作
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)// 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))
println(list2.sorted)// 5.2 sortBy设置排序的方式
println(list2.sortBy(_._2))
println(list2.sortBy(_._2)(Ordering[Int].reverse))// 5.3 sortWith
println(list.sortWith( (a: Int, b: Int) {a b} ))
// 从小到大
println(list.sortWith( _ _ ))
println(list.sortWith( _ _))7.8.4 map操作
val list List(0,1,2,3,4,5,6,7,8,9)// 1. 过滤filter
// 选取偶数
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. 扁平化flatten
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()// 4. 扁平映射flatMap
// 将一组字符串进行分词并保存成单词的列表
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)val flatmapList strings.flatMap(_.split( ))
println(flatmapList)
println()// 5. 分组groupBy
// 分成奇偶两组Map(1 - List(1, 3, 5, 7, 9), 0 - List(0, 2, 4, 6, 8))
val groupMap: Map[Int, List[Int]] list.groupBy( _ % 2)
// Map(奇数 - List(1, 3, 5, 7, 9), 偶数 - List(0, 2, 4, 6, 8))
val groupMap2: Map[String, List[Int]] list.groupBy( data if (data % 2 0) 偶数 else 奇数)println(groupMap)
println(groupMap2)// 给定一组词汇按照单词的首字母进行分组groupBy
// Map(b - List(bob), j - List(japan), a - List(america, alice), c - List(china, canada, cary))
val wordList List(china, america, alice, canada, cary, bob, japan)
println( wordList.groupBy( _.charAt(0) ) )7.8.5 reduce操作
fold和reduce的区别就是fold是具有初始值的是以初始值为主的计算
// 1. reduce 示例为求和
println(list.reduce( _ _ ))
// 从左往右算
println(list.reduceLeft(_ _))
// 从右往左算
println(list.reduceRight(_ _))
println()val list2 List(3,4,5,8,10)
// 从左往右减
println(list2.reduce(_ - _)) // -24
println(list2.reduceLeft(_ - _))
// 底层代码是递归调用
println(list2.reduceRight(_ - _)) // 3 - (4 - (5 - (8 - 10))), 6println()
// 2. fold有初始值
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)))), -57.8.6 map合并
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// 以map2作为初始值底层是递归调用的所以map2应当为可变Map// 因为fold中两个类型必须一样所以使用foldLeftval map3 map1.foldLeft(map2)(// mergedMap表示结果,初始值为map2kv表示的是map1中的元素(mergedMap, kv) {val key kv._1val value kv._2println(kv._1 kv._2)mergedMap(key) mergedMap.getOrElse(key, 0) valuemergedMap}7.8.7 单词统计 val stringList: List[String] List(hello,hello world,hello scala,hello spark from scala,hello flink from scala)// 1. 对字符串进行切分得到一个打散所有单词的列表
// val wordList1: List[Array[String]] stringList.map(_.split( ))
// val wordList2: List[String] wordList1.flatten
// println(wordList2)val wordList:List[String] stringList.flatMap(_.split( ))println(wordList)// 2. 相同的单词进行分组groupBy传入的函数就是(word返回值为它自己)val groupMap: Map[String, List[String]] wordList.groupBy(word word)println(groupMap)// 3. 对分组之后的list取长度得到每个单词的个数val countMap: Map[String, Int] groupMap.map(kv (kv._1, kv._2.length))println(countMap)// 4. 将map转换为list并排序取前3val sortList: List[(String, Int)] countMap.toList.sortWith( _._2 _._2 ).take(3)println(sortList)结果 List(hello, hello, world, hello, scala, hello, spark, from, scala, hello, flink, from, scala) Map(world - List(world), flink - List(flink), spark - List(spark), scala - List(scala, scala, scala), from - List(from, from), hello - List(hello, hello, hello, hello, hello)) Map(world - 1, flink - 1, spark - 1, scala - 3, from - 2, hello - 5) List((hello,5), (scala,3), (from,2))
7.8.8 复杂单词统计
// 1. 将字符串打散为单词并结合对应的个数包装成二元组
val preCountList: List[(String, Int)] tupleList.flatMap(tuple {val strings: Array[String] tuple._1.split( )strings.map( word (word, tuple._2) )}
)
println(preCountList)
// 2. 对二元组按照单词进行分组
val preCountMap: Map[String, List[(String, Int)]] preCountList.groupBy( _._1 )
println(preCountMap)// 3. 叠加每个单词预统计的个数值
val countMap: Map[String, Int] preCountMap.mapValues(tupleList tupleList.map(_._2).sum
)
println(countMap)// 4. 转换成list排序取前3
val countList countMap.toList.sortWith(_._2 _._2).take(3)
println(countList)7.9 队列
// 创建一个可变队列
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)7.10 并行集合
// 串行执行Vector(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1...)
val result: immutable.IndexedSeq[Long] (1 to 100).map(x Thread.currentThread.getId
)
println(result)
// 并行执行ParVector(12, 12, 12, 12, 12, 12, 18, 18, 16, 16, 16, 19, 19...)
val result2: ParSeq[Long] (1 to 100).par.map(x Thread.currentThread.getId
)
println(result2)8 模式匹配
代替了JAVA中的switch case
如果所有 case 都不匹配那么会执行 case _ 分支类似于 Java 中 default 语句 若此时没有 case _ 分支那么会抛出 MatchError。每个 case 中不需要使用 break 语句自动中断 case。match case 语句可以匹配任何类型而不只是字面量。 后面的代码块直到下一个 case 语句之前的代码是作为一个整体执行可以 使用{}括起来也可以不括。
8.1 基本语法
// 1. 基本定义语法
val x: Int 5
val y: String x match {case 1 onecase 2 twocase 3 threecase _ other
}
println(y)// 2. 示例用模式匹配实现简单二元运算
val a 25
val b 13def matchDualOp(op: Char): Int op match {case a bcase - a - bcase * a * bcase / a / bcase % a % bcase _ -1
}println(matchDualOp())
println(matchDualOp(/))
println(matchDualOp(\\))println()8.2 模式守卫
// 3. 模式守卫 也就是加了个判断
// 求一个整数的绝对值
def abs(num: Int): Int {num match {case i if i 0 icase i if i 0 -i}
}println(abs(67))
println(abs(0))
println(abs(-24))8.3 匹配类型
package chapter08object Test02_MatchTypes {def main(args: Array[String]): Unit {// 1. 匹配常量def describeConst(x: Any): String x match {case 1 Int onecase hello String hellocase true Boolean truecase Char case _ }println(describeConst(hello))println(describeConst())println(describeConst(0.3))println()// 2. 匹配类型def describeType(x: Any): String x match {case i: Int Int icase s: String String s// JVM中存在泛型擦除也就是只能识别List,而不能识别其中的类型case list: List[String] List list// 可以识别Array及其内的元素类型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)))// 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) 0case 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)}println()// 4. 匹配列表// 方式一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) 0case List(x, y) List(x, y): x , ycase List(0, _*) List(0, ...)// list中有一个元素case List(a) List(a): acase _ something else}println(result)}// 方式二val list1 List(1, 2, 5, 7, 24)val list List(24)list1 match {//first: 1, second: 2, rest: List(5, 7, 24)case first :: second :: rest println(sfirst: $first, second: $second, rest: $rest)case _ println(something else)}println()// 5. 匹配元组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 , bcase (0, _) (0, _)case (a, 1, _) (a, 1, _) acase (x, y, z) (x, y, z) x y zcase _ something else}println(result)}}
}8.4 应用时匹配
package chapter08object Test03_MatchTupleExtend {def main(args: Array[String]): Unit {// 1. 在变量声明时匹配val (x, y) (10, hello)println(sx: $x, y: $y)val List(first, second, _*) List(23, 15, 9, 78)println(sfirst: $first, second: $second)val fir :: sec :: rest List(23, 15 , 9, 78)println(sfirst: $fir, second: $sec, rest: $rest)println()// 2. for推导式中进行模式匹配val list: List[(String, Int)] List((a, 12), (b, 35), (c, 27), (a, 13))// 2.1 原本的遍历方式for (elem - list){println(elem._1 elem._2)}// 2.2 将List的元素直接定义为元组对变量赋值for ((word, count) - list ){println(word : count)}println(-----------------------)// 2.3 可以不考虑某个位置的变量只遍历key或者valuefor ((word, _) - list)println(word)println(-----------------------)// 2.4 可以指定某个位置的值必须是多少for ((a, count) - list){println(count)}}
}8.5 匹配对象
package chapter08object Test04_MatchObject {def main(args: Array[String]): Unit {val student new Student(alice, 19)// 使用伴生对象针对对象实例的内容进行匹配val result student match {case Student(alice, 18) Alice, 18case _ 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方法用来对对象属性进行拆解 Option[(String, Int)]def unapply(student: Student): Option[(String, Int)] {if (student null){None} else {Some((student.name, student.age))}}
}8.6 样例类
object Test05_MatchCaseClass {def main(args: Array[String]): Unit {val student Student1(alice, 18)// 针对对象实例的内容进行匹配val result student match {case Student1(alice, 18) Alice, 18case _ Else}println(result)}
}// 定义样例类
case class Student1(name: String, age: Int)8.7 偏函数
偏函数也是函数的一种通过偏函数我们可以方便的对输入参数做更精确的检查。例如 该偏函数的输入类型为 List[Int]而我们需要的是第一个元素是 0 的集合这就是通过模式 匹配实现的。 package chapter08object 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)// 偏函数的应用求绝对值// 对输入数据分为不同的情形正、负、0val 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))}
}9 异常
需要注意以下几点
Scala 没有“checked编译期”异常即 Scala 没有编译异常这个概念异常都是在运行的时候捕获处理。所有异常都是 Throwable 的子类型。throw 表达式是有类型的就是 Nothing
def main(args: Array[String]): Unit {try{val n 10 / 0} catch {case e: ArithmeticException {println(发生算术异常)}case e: Exception {println(发生一般异常)}} finally {println(处理结束)}
}def test():Nothing {throw new Exception(不对)
}10 隐式转换
当编译器第一次编译失败的时候会在当前的环境中查找能让代码编译通过的方法用于将类型进行转换实现二次编译
10.1 隐式函数和隐式类
def main(args: Array[String]): Unit {val new12 new MyRichInt(12)println(new12.myMax(15))// 1. 隐式函数将int转换成MyRichInt,需要写在调用代码的前面implicit def convert(num: Int): MyRichInt new MyRichInt(num)println(12.myMax(15))println()// 2. 隐式类必须放在object或者其他类的内部implicit class MyRichInt2(val self: Int) {// 自定义比较大小的方法def myMax2(n: Int): Int if ( n self ) self else ndef myMin2(n: Int): Int if ( n self ) n else self}println(12.myMin2(15))println()
}
// 自定义类
class MyRichInt(val self: Int) {// 自定义比较大小的方法def myMax(n: Int): Int if ( n self ) self else ndef myMin(n: Int): Int if ( n self ) n else self
}10.2 隐式参数
就是可以将参数的默认值定义在函数外部
// 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()
// 隐式参数会覆盖参数的默认值所以输出的是hi atguigu
sayHi// 简便写法implicitly
def hiAge(): Unit {// 指明调用Int的隐式参数println(hi, implicitly[Int])
}
hiAge()11 泛型
11.1 协变和逆变 object Test03_Generics {def main(args: Array[String]): Unit {// 1. 协变和逆变val child: Parent new Child// 协变,Child是Parent的子类如果不使用协变就无法定义//val childList: MyCollection[Parent] new MyCollection[Child]// 逆变,Child是SubChild的子类如果不使用逆变就无法定义val childList: MyCollection[SubChild] new MyCollection[Child]}
}// 定义继承关系
class Parent {}
class Child extends Parent {}
class SubChild extends Child {}// 定义带泛型的集合类型,使用逆变
class MyCollection[-E] {}11.2 泛型上下限
// 2. 上下限,传入的泛型A只能是Child及其子类
def test[A : Child](a: A): Unit {println(a.getClass.getName)
}
// 下面会报错因为Parent不是Child及其子类
// test[Parent](new Child)
test[SubChild](new SubChild)
test[Child](new SubChild)
// 下面会报错因为不能把父类(Child)的对象赋给子类(SubChild)
// test[SubChild](new Child)
// 定义继承关系
class Parent {}
class Child extends Parent {}
class SubChild extends Child {}
11.3 上下文限定
相当于内部包含一个隐式参数B[A]
def f[A : B](a: A) println(a)
//等同于 def f[A](a:A)(implicit arg:B[A])println(a)说明 上下文限定是将泛型和隐式转换的结合产物以下两者功能相同使用上下文限定[A : Ordering]之后方法内无法使用隐式参数名调用隐式参数需要通过 implicitly[Ordering[A]] 获取隐式变量如果此时无法查找到对应类型的隐式变量会发生出错误。
def main(args: Array[String]): Unit {implicit val x 1val y implicitly[Int]// 下面会报错 因为没有隐式的Double参数// val z implicitly[Double]println(y)// 使用上下文限定也就等同于下面的代码相当于有一个隐式的参数参数类型为Ordering[A]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)// 34 结果为-1println(f[Int](3,4))
}9 异常
需要注意以下几点
Scala 没有“checked编译期”异常即 Scala 没有编译异常这个概念异常都是在运行的时候捕获处理。所有异常都是 Throwable 的子类型。throw 表达式是有类型的就是 Nothing
def main(args: Array[String]): Unit {try{val n 10 / 0} catch {case e: ArithmeticException {println(发生算术异常)}case e: Exception {println(发生一般异常)}} finally {println(处理结束)}
}def test():Nothing {throw new Exception(不对)
}10 隐式转换
当编译器第一次编译失败的时候会在当前的环境中查找能让代码编译通过的方法用于将类型进行转换实现二次编译
10.1 隐式函数和隐式类
def main(args: Array[String]): Unit {val new12 new MyRichInt(12)println(new12.myMax(15))// 1. 隐式函数将int转换成MyRichInt,需要写在调用代码的前面implicit def convert(num: Int): MyRichInt new MyRichInt(num)println(12.myMax(15))println()// 2. 隐式类必须放在object或者其他类的内部implicit class MyRichInt2(val self: Int) {// 自定义比较大小的方法def myMax2(n: Int): Int if ( n self ) self else ndef myMin2(n: Int): Int if ( n self ) n else self}println(12.myMin2(15))println()
}
// 自定义类
class MyRichInt(val self: Int) {// 自定义比较大小的方法def myMax(n: Int): Int if ( n self ) self else ndef myMin(n: Int): Int if ( n self ) n else self
}10.2 隐式参数
就是可以将参数的默认值定义在函数外部
// 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()
// 隐式参数会覆盖参数的默认值所以输出的是hi atguigu
sayHi// 简便写法implicitly
def hiAge(): Unit {// 指明调用Int的隐式参数println(hi, implicitly[Int])
}
hiAge()11 泛型
11.1 协变和逆变
[外链图片转存中…(img-9RgQjphE-1696143222645)]
object Test03_Generics {def main(args: Array[String]): Unit {// 1. 协变和逆变val child: Parent new Child// 协变,Child是Parent的子类如果不使用协变就无法定义//val childList: MyCollection[Parent] new MyCollection[Child]// 逆变,Child是SubChild的子类如果不使用逆变就无法定义val childList: MyCollection[SubChild] new MyCollection[Child]}
}// 定义继承关系
class Parent {}
class Child extends Parent {}
class SubChild extends Child {}// 定义带泛型的集合类型,使用逆变
class MyCollection[-E] {}11.2 泛型上下限
// 2. 上下限,传入的泛型A只能是Child及其子类
def test[A : Child](a: A): Unit {println(a.getClass.getName)
}
// 下面会报错因为Parent不是Child及其子类
// test[Parent](new Child)
test[SubChild](new SubChild)
test[Child](new SubChild)
// 下面会报错因为不能把父类(Child)的对象赋给子类(SubChild)
// test[SubChild](new Child)
// 定义继承关系
class Parent {}
class Child extends Parent {}
class SubChild extends Child {}
11.3 上下文限定
相当于内部包含一个隐式参数B[A]
def f[A : B](a: A) println(a)
//等同于 def f[A](a:A)(implicit arg:B[A])println(a)说明 上下文限定是将泛型和隐式转换的结合产物以下两者功能相同使用上下文限定[A : Ordering]之后方法内无法使用隐式参数名调用隐式参数需要通过 implicitly[Ordering[A]] 获取隐式变量如果此时无法查找到对应类型的隐式变量会发生出错误。
def main(args: Array[String]): Unit {implicit val x 1val y implicitly[Int]// 下面会报错 因为没有隐式的Double参数// val z implicitly[Double]println(y)// 使用上下文限定也就等同于下面的代码相当于有一个隐式的参数参数类型为Ordering[A]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)// 34 结果为-1println(f[Int](3,4))
}