作者:高志远
微信:gzy2001
Hadoop生态体系知识串讲
Scala编程语言
一、概述
http://scala-lang.org
专门为计算而生的语言,Scala将(Java后者C++)面向对象设计
和函数式编程
结合在一起的简洁的高级编程语言。而函数式编程强调的是通过传递算子(代码|函数)实现大规模数据集的本地计算。Scala虽然是一门独立的编程语言,但是它可以无缝和Java语言对接。Scala编译的代码可以直接运行在JVM之上。Spark 和 Scala 能够紧密集成,例如 使用Scala语言操作大数据集合的时候,用户可以像是在操作本地数据集那样简单操作Spark上的分布式数据集-RDD(这个概念是Spark 批处理的核心术语),继而简化大数据集的处理难度,简化开发步骤。
.java源文件 ---> java编译器 .class ---> jvm
.scala源文件 ---> scala编译器 .class ---> jvm
二、环境搭建
安装
准备工作
建议JDK1.8以上
C:\Users\Administrator>java -version
java version "1.8.0_161"
Java(TM) SE Runtime Environment (build 1.8.0_161-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.161-b12, mixed mode)
Scala安装
后续课程所使用的Scala版本:
scala-2.11.12.msi
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cnTApE0f-1575721948433)(assets/1573787952798.png)]
配置环境变量
# SCALA_HOME C:\scala
# PATH C:\scala\bin
测试使用
C:\Users\Administrator>scala -version
Scala code runner version 2.11.12 -- Copyright 2002-2017, LAMP/EPFL
三、使用方式
命令窗口
测试使用
REPL命令窗口: R(Read 读取)、E(Evaluation 评估求值)、P(Print 打印)、L(Loop 循环)
C:\Users\Administrator>scala
Welcome to Scala 2.11.12 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_161).
Type in expressions for evaluation. Or try :help.
scala> 1+1
res0: Int = 2
scala> 1+2
res1: Int = 3
1+1
表达式 执行后返回一个名为res0
变量,类型为Int
, 计算结果为2
传统方式
使用文本编辑器 写Scala源文件,手动编译 解释执行
编译指令:
scalac *.scala
解释执行:
scala *
D:\>scalac HelloWorld3.scala
D:\>scala HelloWorld3
Hello World
object HelloWorld3{
def main(args:Array[String]):Unit = {
println("Hello World")
}
}
IDEA集成
需要集成Scala插件
在线安装
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lIvzSJyB-1575721948435)(assets/1573789318905.png)]
离线安装
注意版本
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6qtET4N0-1575721948436)(assets/1573789546245.png)]
四、变量
表达式
scala> 1+1
res0: Int = 2
scala> 1+2
res1: Int = 3
scala> 1*10
res2: Int = 10
声明变量
语法:
var 变量名:类型 = 值
注意: scala中值类型
Byte、Short、Int、Long、Float、Double、Char、Boolean
【对象】
- 所有的值类型都归属于
scala.*
包- String类型延续了
java.lang.String
- 值类型的默认类型:整数(Int)、小数(Double)
- 类型支持
自动推断
var a:Int = 10
var b:String = "Hello"
var c:Boolean = false
# 如以下语法:
scala> var a:Int = 10
a: Int = 10
scala> var b:java.lang.String = "aa"
b: String = aa
scala> var d = 1
d: Int = 1
scala> var e = 1.0
e: Double = 1.0
// ...
声明常量
常量只允许赋值一次
语法:
val 常量名:类型= 值
注意:
- 创建对象时,建议使用常量
val
scala> val f = 10
f: Int = 10
# 常量只能赋值一次
scala> f = 11
<console>:12: error: reassignment to val
f = 11
类型转换
通俗理解: 自动类型提升
规则:
Byte ---> Short ---> Int ---> Long ---> Float ---> Double
char
值类型可以按照下面的方向进行转换:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dRH9J08g-1575721948437)(assets/20191114100636493.png)]
scala> var a : Byte = 10
a: Byte = 10
scala> var b: Short = a
b: Short = 10
scala> var c : Int = b
c: Int = 10
scala> var d :Short = c
<console>:12: error: type mismatch;
found : Int
required: Short
var d :Short = c
^
scala> var e : Int = 'A'
e: Int = 65
scala> var e : Int = 'a'
e: Int = 97
scala> var f : Double = e
f: Double = 97.0
运算符
- 赋值运算符:
=
- 关系运算符:
> 、< 、<= 、>= 、!=、 ==
- 逻辑运算符:
&& 、 || 、!
- 位运算符:
& 、| 、~ 、^、<<、 >>、 >>>
- 算术运算符:
+、-、*、/、%
注意:scala没有
++
和--
语法,但是依然保留了+=
和-=
//---------------------------------------
// 关系运算符
scala> 10 > 8
res3: Boolean = true
scala> 10 <= 10
res4: Boolean = true
scala> 10 != 9
res5: Boolean = true
scala> 10 ==7
res6: Boolean = false
//---------------------------------------
//---------------------------------------
// 逻辑运算符
scala> 1==1 && 3>2
res7: Boolean = true
scala> 1==1 && 3<2
res8: Boolean = false
scala> 1==1 || 3<2
res9: Boolean = true
//---------------------------------------
//---------------------------------------
// 位运算符
// 按照值得二进制进行位计算(效率高)
// 1 & 2 = 0
// 1 | 2 = 3
// 1: 00000001
// 2: 00000010
// 注意: & 二进制位同时出现1 结果为1 不同为0
// | 二进制位有一个为1 结果为1 不同为0
// ^
// ~
// <<
// >> 和 >>>
scala> 1&2
res12: Int = 0
scala> 1|2
res13: Int = 3
//---------------------------------------
scala> 1+1
res0: Int = 2
// scala是纯面向对象的 运算符都是值类型中方法
scala> 1.+(1)
res1: Int = 2
Scala类型层次结构【重点】
在Scala中,所有的值都有类型,包括数值和函数。下图阐述了类型层次结构的一个子集。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1zPfgUfv-1575721948438)(assets/20191114100738761.png)]
Any
是所有类型的超类型,也称为顶级类型。它定义了一些通用的方法如equals
、hashCode
和toString
。Any
有两个直接子类:AnyVal
和AnyRef
。AnyVal
代表值类型。有9个预定义的非空的值类型分别是:Double
、Float
、Long
、Int
、Short
、Byte
、Char
、Unit
和Boolean
。Unit
(类似于java中的void
)是不带任何意义的值类型,它仅有一个实例可以像这样声明:()
。所有的函数必须有返回,所以说有时候Unit
也是有用的返回类型。AnyRef
代表引用类型。所有非值类型都被定义为引用类型。在Scala中,每个用户自定义的类型都是AnyRef
的子类型。如果Scala被应用在Java的运行环境中,AnyRef
相当于java.lang.Object
。Nothing
是所有类型的子类型,也称为底部类型。没有一个值是Nothing
类型的。它的用途之一是给出非正常终止的信号,如抛出异常、程序退出或者一个无限循环(可以理解为它是一个不对值进行定义的表达式的类型,或者是一个不能正常返回的方法)。Null
是所有引用类型的子类型(即AnyRef
的任意子类型)。它有一个单例值由关键字null
所定义。Null
主要是使得Scala满足和其他JVM语言的互操作性,但是几乎不应该在Scala代码中使用。我们将在后面的章节中介绍null
的替代方案。
这里有一个例子,说明了字符串、整型、布尔值和函数都是对象,这一点和其他对象一样:
val list: List[Any] = List(
"a string",
732, // an integer
'c', // a character
true, // a boolean value
() => "an anonymous function returning a string"
)
list.foreach(element => println(element))
这里定义了一个类型List<Any>
的变量list
。这个列表里由多种类型进行初始化,但是它们都是scala.Any
的实例,所以可以把它们加入到列表中。
下面是程序的输出:
a string
732
c
true
<function>
注意:
在scala的命令窗口,如果想写入多行代码,可以使用
:paste
模式,多行代码写入完成后,按ctrl+D
键退出执行
五、分支语句
类似于Java的分支
if语句
# 语法:
if(布尔条件){
// 内容
}
//-------------------------------------
scala> val age = 18
age: Int = 18
scala> :paste
// Entering paste mode (ctrl-D to finish)
if(age > 16){
println("青年")
}
// Exiting paste mode, now interpreting.
青年
if…else语句
# 语法:
if(布尔条件){
//为true执行
}else{
//为false执行
}
//--------------------------------------
scala> :paste
// Entering paste mode (ctrl-D to finish)
if(age>16){
println("青年")
}else{
println("未成年")
}
// Exiting paste mode, now interpreting.
未成年
if … else if …else语句
#语法:
if(布尔条件1){
// true 执行
}else if(布尔条件2){
// true 执行
}else{
// 以上条件不匹配 执行
}
//------------------------------------
val b =
if(age>18){
println("青年")
}else if(age>10){
println("少年")
}else{
println("儿童")
}
//------------------------------------
val d =
if(age>18){
age + 1
}else if(age>10){
age + 2
}else{
age + 3
}
// val d:Int = 16
//------------------------------------
val f =
if(age>18)age + 1
else if(age>10) age + 2
else age + 3
// val f:Int = 16
总结:
- scala中所有的表达式都是有值得,如果表达式的最后一行内容为打印语句,则表达式的值为
() Unit
类型- scala中代码块的最后一行内容为表达式的值
- scala中代码块中有多行内容时,必须使用大括号
{}
包裹起来,如果只有一行内容,{}
可以省略
模式匹配可以代替switch语句
注意:
- scala中并没有提供switch语句,但是提供了一个功能远超于switch语句的语法,叫做
模式匹配
# 语法:
变量名 match{
case 值 => 处理内容
case 值 => 处理内容
case _ => 处理内容
}
注意:
1. `_` 类似于default
2. 模式匹配不存在匹配穿透问题
//---------------------------------------------------------------
val num = 1
num match{
case 1 => println(1)
case 2 => println(2)
case _ => println("其它")
}
六、循环
while
# 语法:
while(循环条件){
// 循环体
}
// 1~100数值之和
var n = 1
var sum = 0
while(n <= 100){
sum += n
n += 1
}
//------------------------------------------------------
scala> :paste
// Entering paste mode (ctrl-D to finish)
var n = 1
var sum = 0
while(n <= 100){
sum += n
n += 1
}
// Exiting paste mode, now interpreting.
n: Int = 101
sum: Int = 5050
do…while…
# 语法:
do{
// 循环体
}while(循环条件)
// 1~100数值之和
var n = 1
var sum = 0
do{
sum += n
n += 1
}while(n <= 100)
//--------------------------------------------------
scala> :paste
// Entering paste mode (ctrl-D to finish)
var n = 1
var sum = 0
do{
sum += n
n += 1
}while(n <= 100)
// Exiting paste mode, now interpreting.
n: Int = 101
sum: Int = 5050
for
# java for
for(int i = 0; i<=100;i++){
// 循环体
}
# scala for
for(i <- 0 to 100){
println(i)
}
//-----------------------------------------
var sum = 0
for(i <- 0 to 100){ // 数值区间Range(0~100),包含起始和结束数值
sum += i
}
println(sum)
var sum = 0
for(i <- 0 until 100){ // 数值区间Range(0~99),包含起始和不包含结束数值
sum += i
}
println(sum)
//-----------------------------------------
// 通过for循环实现九九乘法表
for(m <- 1 to 9){
for(n <- 1 to m){
print(n+"*"+m+"="+(m*n) +"\t")
}
println()
}
1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
1*4=4 2*4=8 3*4=12 4*4=16
1*5=5 2*5=10 3*5=15 4*5=20 5*5=25
1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36
1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49
1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64
1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81
嵌套for循环
嵌套for循环的简化写法
# 嵌套for语法
# 多重循环在for的()中用`;`分割
for(m <- 1 to 9; n <- 1 to m){
if( m != n){
print(n+"*"+m+"="+(m*n) +"\t")
}else{
println(n+"*"+m+"="+(m*n) +"\t")
}
}
迭代的步长
scala> 1 to 10 by 2
res24: scala.collection.immutable.Range = Range(1, 3, 5, 7, 9)
scala> 1 to 10 by 3
res25: scala.collection.immutable.Range = Range(1, 4, 7, 10)
// 统计 1 ~ 100 偶数之和
var sum = 0
for( n <- 0 to 100 by 2){
sum += n
}
println(sum) // 2550
if守卫
// 通过循环计算 1~10 奇数之和
var sum = 0
for(n <- 1 to 10){
if( n % 2 != 0){
sum += n
}
}
// if守卫简化实现
var sum = 0
for(n <- 1 to 10 if n % 2 != 0){
sum += n
} // 25
// n 必须是奇数 并且n不能是5
var sum = 0
for(n <- 1 to 10 if n % 2 != 0 && n != 5){
sum += n
} // 20
// n 是奇数 或者n可以使6
var sum = 0
for(n <- 1 to 10 if n % 2 != 0 || n == 6){
sum += n
} // 31
break
Java break: 表示结束循环
注意:scala中没有
break
和continue
关键字
import scala.util.control.Breaks._
breakable {
for (n <- 1 to 10) {
if (n == 5) break() // 在scala中如果是一个无参的方法,可以省却(),直接写方法名
println(n)
}
}
yield
for 循环中的 yield 会把当前的元素记下来,保存在集合中,循环结束后将返回该集合
- 首先会将
yield
表达式的值会被加入存放到一个缓冲区中 - 在遍历结束之后,会返回一个基于缓冲区的新集合,类型为
Vector
- 新集合的元素的类型和遍历集合的类型是一致的
val newColle = for(n <- 1 to 10 if n % 2 == 1 && n != 5) yield n*3
// 1 3 7 9
// newColle[3 9 21 27]
//--------------------------------------------------------------------
scala> val newColle = for(n <- 1 to 10 if n % 2 == 1 && n != 5) yield n*3
newColle: scala.collection.immutable.IndexedSeq[Int] = Vector(3, 9, 21, 27)
任务:熟悉scala的基本语法,corejava前三个章节的联系题(scala重构)
七、函数
函数是一段特定功能的代码块
三要素: 函数名、参数列表、返回值
public static 返回值类型 m1(参数列表){ // 函数体 return xxx; }
函数声明
语法:
def 函数名(参数列表):返回值类型 = { // 函数体 } // 如: def main(args:Array[String]):Unit ={ // 主函数内容 }
package function
object Function01 {
// main 快速生成main函数
def main(args: Array[String]): Unit = {
// 如何调用普通函数
println(sum(1, 2))
println(sum2(1, 2))
println(sum3(1, 2)) // sum ~ 3 sum2 ~ "3" sum3 ~ 3 ()
println(sum5(1, 2)) // sum ~ 3 sum2 ~ "3" sum3 ~ 3 () ()
}
// 声明普通函数
def sum(x: Int, y: Int): Int = {
val total = x + y // 自动将代码块的最后一行内容作为返回值
total
}
// 声明普通函数【简化】
def sum2(x: Int, y: Int) = { // 省略返回值类型 `:Int` 自动推导返回值的类型
val total = x + y // 自动将代码块的最后一行内容作为返回值
total + ""
}
// 无返回值的函数 Unit类型的值为()
def sum3(x: Int, y: Int): Unit = {
println(x + y)
}
// 无返回值的函数 Unit类型的值为() 【简化写法】 省略`: Unit =`
def sum4(x: Int, y: Int) {
println(x + y)
}
// 声明普通函数
def sum5(x: Int, y: Int){
val total = x + y // 自动将代码块的最后一行内容作为返回值
total
}
}
总结:
- 有返回值的函数,在声明时可以省略
:返回值类型
,scala自动根据类型推导返回值类型- 无返回值的函数,在声明时可以省略
:返回值类型=
, scala会自动认为是无返回值的函数
变长参数
在java中如果声明可变长参数
public static void m1(String ... args)
在scala中如果声明可变长参数
def 函数名(args:Int*):返回值类型 ={}
package function
/**
* 可变长参数
*/
object Function02 {
def main(args: Array[String]): Unit = {
println(m1(1, 2, 3, 4, 5)) // 15
println(m1(1, 2, 3, 4, 5, 6, 7)) // 28
println(m2(1, 1,2, 3, 4, 5, 6, 7)) // 29
}
// 声明一个Int类型的可变长参数 如:计算参数列表的整数之和
def m1(args: Int*): Int = { // 数组
var total = 0
for (n <- args) {
total += n
}
total
}
// 注意: 在定义可变长参数时,可变长参数需要放置在函数的参数列表之后
def m2(init: Int, args: Int*) = {
var default = init
for (n <- args) {
default += n
}
default
}
}
参数默认值
scala的函数式可以有默认值的,在使用时
- 如果调用传值,使用传值进行计算
- 如果调用没有传值,则使用默认值计算
def sum(x: Int, y: Int, z: Int = 10): Int = { // 函数的参数有默认值的`参数名:类型名 = 默认值`
x+y+z
}
带名参数
在调用函数时,给指定参数名的参数赋值
println(sum2(x = 1, z = 2)) // 13 【使用带名参数】
println(sum2(z = 2, x = 1)) // 13 【使用带名参数】 注意:带名参数使用时与函数的参数列表的顺序无关
def sum2(x: Int, y: Int = 10, z: Int): Int = { // 函数的参数有默认值的`参数名:类型名 = 默认值`
x + y + z
}
Lazy值(懒加载)
在scala中变量或者常量可以被声明为懒加载的值,值在第一次使用时,才会进行初始化
package function
import scala.io.Source._
/**
* 懒加载
*/
object Function04 {
def main(args: Array[String]): Unit = {
// 语法 lazy 变量或者常量的声明
// 立即进行初始化 ERROR
// val source = fromFile("D://abc.txt")
// 懒加载的值
lazy val source2 = fromFile("D://abc.txt")
// 使用时,懒加载的值该初始化了,ERROR
source2.mkString
}
}
递归函数
类似于JAVA的递归函数
// 计算1~10的阶乘
package function
/**
* 递归函数(求1~n阶乘)
*/
object Function05 {
def main(args: Array[String]): Unit = {
println(m1(3)) // 6
println(m2(5))
}
// 求1~n的整数之和
def m1(n: Int): Int = {
var total = 0
if (n > 0) {
total += n + m1(n - 1)
}
total
}
// 第一次递归: n <= 0 total = 0
// 第二次递归: n = 1 total = 0 + 1
// 第三次递归: n = 2 total = 0 + 1 + 2
// 第四次递归: n = 3 total = 0 + 1 + 2 + 3 = 6
def m2(n: Int): Int = {
var total = 1
if (n >= 1) {
total *= n * m2(n - 1) // 1*2*3*4*5 = 120
}
total
}
// 第一次递归:
}
匿名函数【重点】
没有函数名的函数
语法:
参数列名 => 函数体
注意: 内置了23个匿名函数对象
Function0~Function22
, 匿名函数的背后对应的是一个函数对象
(x:Int,y:Int) => x+y
package function
/**
* 匿名函数
*/
object Function06 {
def main(args: Array[String]): Unit = {
// 匿名函数声明
val f2: Function2[Int, Int, String] = (x: Int, y: Int) => x + y + " "
// 匿名函数使用
println(f2(1, 2))
println("-------------------------------------")
val f1: Function1[Int, Unit] = (n: Int) => println(n)
val list = List(1, 2, 3, 4, 5)
// 将函数对象作为方法的参数
list.foreach(f1)
println("-------------------------------------")
list
.filter((n: Int) => n != 3) // 保留集合中符合条件的数据
.foreach((n: Int) => println(n)) // 1 2 4 5
}
}
柯里化(Currying)
定义:
柯里化函数指将接受多个参数的函数,改造为接受一个参数的函数,并且返回一个函数对象的过程
package function
/**
* 柯里化函数的定义
*
* 柯里化函数指将接受多个参数的函数,改造为接受一个参数的函数,并且返回一个函数对象的过程
*/
object Function08 {
def main(args: Array[String]): Unit = {
println(sum(1, 2)) // 3
println(sum2(1)) // Function1
println(sum2(1)(2)) // 结果:3 第一步: sum2(1) <==> (y:Int) => 1+y 第二步:sum2(1)(2) <==> (2) => 1+2
println(sum3(1)(2)) // 结果:3 第一步: sum2(1) <==> (y:Int) => 1+y 第二步:sum2(1)(2) <==> (2) => 1+2
}
// 普通函数
def sum(x: Int, y: Int) = {
x + y
}
// 柯里化函数
def sum2(x: Int) = { // 返回值: 函数对象Function1
(y: Int) => x + y
}
// 柯里化函数【简化】 省略了 `返回值类型信息` 将匿名函数的连接符 修改为了 = 即可
def sum3(x: Int)(y: Int) = {
x + y
}
def sum4(x: Int) = {
(y: Int) => (z: Int) => x + y + z // sum4 接受单个参数的普通函数
// sum4 函数体内 匿名函数 (y:Int) => //匿名函数体
// 匿名函数体又是一个匿名函数 (z:Int) => x+ y+z
}
def sum5(x:Int)(y:Int)(z:Int) ={
x+y+z
}
}
八、数组
数组定义: 数据的集合,在内存中表示为一段连续的存储空间
数组的声明
// java int[] arr = new int[]{1,2,3,4,5}
// int [] arr = new int[10]
// int arr[] = {1,2,3,4,5}
// 语法: val arr = Array[泛型](元素列表)
// scala数组的声明
val arr = Array[Int](1, 2, 3, 4, 5)
数组的使用
- 长度(length): 1~n
- 下标(index) : 0~ n-1
// 遍历
package array
object Array01 {
def main(args: Array[String]): Unit = {
// scala数组的声明
val arr = Array[Int](1, 2, 3, 4, 5)
// 遍历方法1
for(n <- 0 until arr.length){
println(arr(n)) // 注意: 获取数组中元素使用的是 数组名(下标)
}
println("-----------------------------------")
// 遍历方法2
for(n <- arr){
println(n)
}
// 修改数组中的元素
arr(3) = 10
println("-----------------------------------")
// 遍历方法3
for(n <- arr.indices){ // indices 获取到的是数组的下标数值区间
println(arr(n))
}
}
}
数组的排序
// 冒泡排序
package array
/**
* 冒泡排序
*
* Array(5,10,2,1,6,7)
*
* 第一次:
* 5 10 2 1 6 7
* 5 2 10 1 6 7
* 5 2 1 10 6 7
* 5 2 1 6 10 6
* 5 2 1 6 7 10
* 第二次:
* ...
*/
object Array02 {
def main(args: Array[String]): Unit = {
val arr = Array(5,10,2,1,6,7)
// for(m <- 0 until arr.length){
// for(n <- 0 until arr.length - m -1){
// if(arr(n) > arr(n+1)){
// val tmp = arr(n)
// arr(n) = arr(n+1)
// arr(n+1) = tmp
// }
// }
// }
// 改造代码【嵌套 + if守卫】
for(m <- 0 until arr.length; n <- 0 until arr.length - m -1 if arr(n) > arr(n+1)){
val tmp = arr(n)
arr(n) = arr(n+1)
arr(n+1) = tmp
}
for( m <- arr){
println(m)
}
}
}
可变数组ArrayBuffer
Array 定长数组,一旦声明 数据长度就固定不可变
ArrayBuffer 可变数组,类似于集合,数组会自动扩容
package array
import scala.collection.mutable.ArrayBuffer
/**
* 可变数组
*/
object Array03 {
def main(args: Array[String]): Unit = {
val ab = ArrayBuffer(1, 2, 3, 4, 5)
// 可变数组添加元素
ab += 6
ab.+=(7)
for(n <- ab){
println(n)
}
}
}
Array和ArrayBuffer相互转换
// arrayBuffer ---> array 可变到定长的转换
val arr = ab.toArray
// array ---> arrayBuffer 定长到可变长的转换
arr.toBuffer
多维数组
多维: 数组的元素又是数组
package array
/**
* 二维数组
*/
object Array04 {
def main(args: Array[String]): Unit = {
val arr = Array.ofDim[Int](10, 4) // 类似于10行4列的二维表
// 操作
arr(1)(2) = 2
// 获取
println(arr(1)(2))
for (m <- 0 to arr.length - 1) {
for (n <- 0 to arr(m).length - 1) {
print(arr(m)(n) + "\t")
}
println()
}
}
}
练习
2:00~2:30
选择排序
算法案列:移除第一个负数后的所有数
- 测试数据
val a1=Array(1,2,3,4,5,-5,-4,-3)
- 预期结果
val a2=Array(1,2,3,4,5,-5)
九、类
类(class):对象的模板,成员(有什么)和方法(做什么)
简单类
语法: class 类名{ // 成员或者方法 }
// 声明一个Animals
class Animals{}
// 创建对象
val animals:Animals = new Animals()
或
val animals:Animals = new Animals
//---------------------------------------
classes.Animals@47f37ef1
classes.Animals@5a01ccaa
注意:
- 类中提供一个默认的无参的主构造器
- 如果创建对象时调用是无参的构造方法,
()
可以省略
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PKOdklZi-1575721948440)(assets/1574062256404.png)]
成员和方法
成员
注意:
- scala中成员全部都是私有的(
private
)- scala中成员的getter方法名
成员名()
- scala中成员的setter方法名
成员名_$eq(参数)
// 如何声明成员,语法:
// ① 声明变量成员, 都是私有的成员,scala自动为成员提供公开的setter【id_$eq | name_$eq】/getter【id() | name()】方法
var id: Int = 0 // 必须指定初始值
var name: String = ""
// ② 声明常量成员,依然是私有的成员,scala自动为成员提供公开的getter【sex()】方法
val sex: Boolean = false
// 变化一 private
// ③ 成员private修饰的变量,依然是私有的成员,scala自动为成员提供私有的getter/setter方法
private var birthday = new java.util.Date()
// 变化二 private
// ④ 成员private修饰的常量,依然是私有的成员,scala自动为成员提供私有的getter方法
private val address = ""
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vb8IbRJB-1575721948441)(assets/1574064931000.png)]
方法
语法: 和函数的语法一样
package classes
/**
* scala方法
*/
object ColorTest {
def main(args: Array[String]): Unit = {
val red: Red = new Red
println(red.display())
println(red)
}
}
class Red {
var name: String = "red"
// 方法
def display(): String = {
this.name
}
override def toString: String ="红色"
}
手动生成getter/setter方法
语法:
getter方法 ---->
def 区分成员名() = 成员值
setter方法 ---->
def 区分成员名_(形参) {成员值 = 形参值}
package classes
object PersonTest {
def main(args: Array[String]): Unit = {
val person = new Person
person.setName_("zs")
println(person.getName)
}
}
class Person {
// 自定义name成员的getter和setter方法
var name: String = ""
// 自定义getter
def getName(): String = this.name
// 自定义setter方法
def setName_(newName: String): Unit = this.name = newName
}
@BeanProperty
此注解加载成员的前面,scala编译时,会自动生产java版本和scala版本的getter/setter方法
package classes
import scala.beans.BeanProperty
object CourseTest {
def main(args: Array[String]): Unit = {
val chinese = new Chinese
chinese.getCourseName
chinese.setCourseName("文言文")
chinese.courseName = "四书五经" // scala风格setter方法
val courseName = chinese.courseName
println(courseName)
}
}
class Chinese {
@BeanProperty var courseName: String = "中文"
}
辅助构造器
辅助构造器,类似于java重载的构造方法
注意:scala的一个类中,可以有一个主构造器和若干个辅助构造器
package classes
object FishTest {
def main(args: Array[String]): Unit = {
val fish1 = new Fish() // 调用无参数的主构造器
val fish2 = new Fish("金鱼") // 调用无参数的主构造器
val fish3 = new Fish("金鱼", "黄色") // 调用无参数的主构造器
println(fish1)
println(fish2)
println(fish3)
//----------------------------
/*
金鱼
金鱼 黄色
*/
//----------------------------
}
}
// scala默认提供一个无参的主构造器
class Fish {
var name: String = ""
var color: String = ""
// 辅助的构造器
// 注意:辅助构造器必须以调用一个主构造器或者辅助构造器开始
def this(name: String) {
this()
this.name = name
}
def this(name: String, color: String) {
this(name)
this.color = color
}
override def toString: String = this.name + "\t" + this.color
}
注意事项:
- 辅助构造器,使用的关键字
this
- 辅助构造器内,第一行必须调用主构造器或者辅助构造器
主构造器
- 声明,主构造器的参数列表直接写在类名之后:
class Fish(var name:String,var color:String){}
- 主构造器的方法体,是整个类,也就是类的
{}
的所有内容,注意主构造器方法体中的成员和方法仅仅是声明,并不会执行
package classes
object PandaTest {
def main(args: Array[String]): Unit = {
val p1 = new Panda("小黑","熊猫色")
println()
val p2 = new Panda("小黑","熊猫色")
}
}
// 主构造器
class Panda(var name: String, var color: String) {
var age: Int = 0
// 方法体
println("before")
def sayHello(str: String): String = {
str + name
}
println("after")
}
//class Panda{
// private String name;
// private String color;
// private Int age;
// public Panda(String name,String color){
// this.name = name;
// this.color = color;
// }
//}
十、对象(object)
代替java中
static
语法的,因为scala没有static
,如果需要在scala的源文件中定义静态的成员和方法,使用object
类型
单例对象
不管创建多少次,只存在一个对象实例
总结:
- scala中一个类,类型为object,此类即为单例对象
- 类体中成员和方法,都是静态成员【所有类共享的】
package objectes
/**
* scala中一个类,类型为object,此类即为单例对象
*/
object SingletonObject {
// 静态属性
var id: Int = 0
// 静态方法
def incrementId(): Int = {
this.id = this.id +1
this.id
}
def main(args: Array[String]): Unit = {
val s1 = SingletonObject
val s2 = SingletonObject
val s3 = new SingletonObject()
val s4 = new SingletonObject()
println(s1 == s2) //true【单例】
println(s3 == s4) //false【多例】
val id1 = s1.incrementId()
val id2 = s2.incrementId()
println(id1+"\t"+id2)
}
}
class SingletonObject {
}
伴生对象&伴生类
在一个scala的源文件中,class的名字和object名字一样,class类就被称为伴生类,object类就称为伴生对象
package objectes
/**
* 伴生类&伴生对象
*
* 注意:伴生类可以访问伴生对象的私有成员
*
* java中: 普通方法能不能调用静态方法或者成员 【可以】
*/
class Person {
def sayHi(): String = {
"Hi:" + Person.name + "\t" + Person.id
}
}
object Person {
val id: Int = 0
private val name: String = "zs"
def main(args: Array[String]): Unit = {
val person = new Person
println(person.sayHi())
}
}
如
List(1,2,3)
类型
apply & unapply
apply 方法是object类型中的一个方法,通常作为工厂方法创建伴生类的对象【简化方式】
结论:
** 调用的伴生对象中的apply方法,用以创建伴生类**
package objectes
class Cat(var name: String, var color: String) {
}
object Cat {
// apply方法创建伴生类
def apply(name: String, color: String): Cat = new Cat(name, color)
def main(args: Array[String]): Unit = {
// 创建Cat对象
val cat = new Cat("小花", "white")
// apply 工厂方法,用以创建类的对象,通常定义在伴生对象中
// 创建伴生类对象时【简化写法】
val cat2 = Cat("小黑","black") // 等价于Cat.apply("小黑","black")
val cat3 = Cat("小黑","black") // 等价于Cat.apply("小黑","black")
println(cat2 == cat3)
}
}
unapply: 伴生对象中的方法,作用和apply方法相反,接受一个伴生类对象,返回伴生类对象的成员
package objectes
class Cat(var name: String, var color: String) {
}
object Cat {
// apply方法创建伴生类
def apply(name: String, color: String): Cat = new Cat(name, color)
// 将伴生类对象 拆箱为成员
// Option 【避免出现空指针异常,有值 Some 无值 None】
// - None
// - Some
def unapply(arg: Cat): Option[(String, String)] = {
if (arg == null) None
else {
Some((arg.name, arg.color)) //(name,color) scala元组,可以存放任意类型的list集合 类型与java中的ArrayList[Object]
}
}
def main(args: Array[String]): Unit = {
// 创建Cat对象
val cat = new Cat("小花", "white")
// apply 工厂方法,用以创建类的对象,通常定义在伴生对象中
// 创建伴生类对象时【简化写法】
val cat2 = Cat("小黑", "black") // 等价于Cat.apply("小黑","black")
val cat3 = Cat("小黑", "black") // 等价于Cat.apply("小黑","black")
println(cat2 == cat3)
// new Array[Int](10)
// Array(1,2,3,4)
// List(1,2,3,4,5) 调用的伴生对象中的apply方法,用以创建伴生类
// ================================================
// unapply方法应用
val Cat((name, color)) = cat3 // Cat.unapply(cat3) = Some(cat3.name,cat3.color)
// val name = cat3.name
// val color = cat3.color
println(name + "\t" + color)
// ================================================
}
}
十一、继承
Java中的继承,子类继承父类等价于继承了父类成员和方法,使用的关键字
extends
,单继承
基本语法
package extendses
class Animals {
// 私有成员,公开的getter/setter方法,
var name: String = ""
def sayHi(): String = {
"Hello:" + name
}
}
class Dog extends Animals {
}
object Dog {
def main(args: Array[String]): Unit = {
val dog = new Dog
dog.name = "小黑" // 父类的setter
println(dog.sayHi()) // 父类的sayHi
}
}
注意:
- scala的继承同样使用的是
extends
- scala的子类同样可以继承父类中的成员和方法
方法覆盖
覆盖:父类定义的方法无法子类需求时,在子类中定义相同的方法,在调用时,以子类优先
关键字
override
,不可省略
package extendses
class Person {
def run(): Unit = {
println("人在奔跑!")
}
}
class SmallStudent extends Person {
// 父类方法的覆盖 `override`
override def run(): Unit = println("小学生在学走路!")
}
object SmallStudent {
def main(args: Array[String]): Unit = {
val student = new SmallStudent
student.run() // 小学生在学走路!
}
}
类型检查和转换
java:
isInstanceof
if(dog isInstanceof Animals)scala :
判断类型是否兼容:
isInstanceOf[类型]
类型转换语法:
asInstanceOf[另外一种类型]
val bool = student.isInstanceOf[Person]
println(bool) // true
val person = new Person
val bool2 = person.isInstanceOf[SmallStudent]
println(bool2) // false
//-------------------------------------------
// 将小学生的类型 赋值父类型
val person2: Person = student.asInstanceOf[Person]
// 因为person2的真实类型是SmallStudent
val student2 = person2.asInstanceOf[SmallStudent]
调用父类有参构造
结论:
- 如果调用父类的有参构造方法,需要在extends 父类
(父类的有参构造)
- 子类的主构造器中,必须有父类的成员,并且不需要声明为
val
或者var
package extendses
// color父类没有无参的主构造器,只有一个有一个参数的主构造器
// scala中没有`Super()`语法,子类必须得调用父类的有参构造
class Color(var name: String) {
println("父类")
}
class Red(name: String) extends Color(name: String) {
println("子类")
}
object Red {
def main(args: Array[String]): Unit = {
val red = new Red("红色")
println(red.name)
}
}
//class Color{
// String name;
// public Color(String name){
// this.name = name;
// }
//}
//
//class Red extends Color{
//
// public Red(){
// super("red")
// }
//}
重写字段
结论:
- 如果子类需要覆盖父类中的变量(var),子类中不需要var关键字和变量的类型
- 如果子类需要覆盖父类中的常量(val),子类必须在父类常量的基础之上加上
override
关键字- 如果父类中的私有成员(private),子类是无法覆盖,原因是:父类中属性和getter/setter方法都是私有的
package extendses
class A {
var id: Int = 0
val name: String = ""
private var sex: Boolean = false // getter/setter私有的方法
}
class B extends A {
// 子类覆盖父类成员id/name
// 覆盖重写变量 id, 注意:省略了变量的关键字`var`和变量的类型
id = 10
// 覆盖重写常量 name
override val name: String = "BB"
private var sex: Boolean = true // 并不是继承,子类中特有的成员
}
object B {
def apply(): B = new B()
def main(args: Array[String]): Unit = {
val b = B()
println(b.id + "\t" + b.name + "\t" + b.sex) //10 BB true
}
}
抽象类
类似于java的抽象类,使用的关键字依然是
abstract
- 抽象类,可能有抽象成员和普通成员
- 子类继承抽象类,必须得实现抽象类的声明方法,如果未实现,子类依然是一个抽象类
- 抽象类只能声明引用而不能创建对象
- 在实现抽象类的抽象方式或者属性时,依然使用的是关键字
override
package extendses
// 抽象类
abstract class Person2 {
// 抽象字段
val name: String
// 抽象方法
def playGame(): String
}
object Person2Test {
def main(args: Array[String]): Unit = {
//1. 抽象类只能声明引用而不能创建对象
val person1:Person2 = new SmallStudent2
println(person1.name +"\t" +person1.playGame)
val person2:Person2 = new HighStudent2
println(person2.name +"\t" +person2.playGame)
}
}
class SmallStudent2 extends Person2 {
override val name: String = "小学生"
override def playGame(): String = "王者荣耀"
}
abstract class MiddleStudent2 extends Person2 {
override def playGame(): String = "英雄联盟"
}
class HighStudent2 extends Person2 {
override def playGame(): String = "绝地求生"
override val name: String = "高中生"
}
匿名子类
类似于java中的匿名内部类
abstract class A2 {
def m1(): Unit
}
class B2 extends A2 {
def m1(): Unit = {
// 方法体
}
}
// 匿名子类
val a2: A2 = new A2 {
override def m1(): Unit = println("m1")
}
a2.m1()
包可见性(访问修饰符)
Java访问修饰符: public、default(默认)、protected、private
Scala访问修饰符:default(public)、protected、private
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Mlkqolg9-1575721948442)(assets/20171013163614831.png)]
package extendses
class AA {
// 默认访问修饰符 public:全部
var name: String = ""
// protected范围:本类、伴生对象、子类
protected var sex: Boolean = false
// private范围:本类、伴生对象
private var address: String = ""
}
object AA {
def main(args: Array[String]): Unit = {
val aa = new AA
aa.address // private 伴生对象ok
aa.sex // protected 伴生对象ok
}
}
class BB extends AA {
def m1(): Unit = {
sex // protected 子类 OK
// address // private 子类 ERROR
}
}
class DD{
def m2(): Unit ={
// new AA().sex() //error
new AA().name //ok
}
}
十二、特质(trait)
特质: 类似于Java中的接口
语法
-
特质关键词trait
-
特质可以同时用于抽象方法和具体方法
-
无父类 实现特质
extends trait1 with trait2 with trait3 ...
-
有父类 实现特质
extends 父类 with 特质1 with 特质2 ...
当做接口的特质
package traites
/**
* 等价于Java接口
* 1. 公开静态常量
* 2. 公开抽象方法
* 3. JDK8之后,接口中也可以由具体的方法
*/
trait Person {
// 特质中的属性
var name: String
// 特质中的一个抽象方法
def run(): String
}
class Father
class SmallStudent extends Person {
override var name: String = "小学生"
override def run(): String = name + "在走路!"
}
class MiddleStudent extends Father with Person{
override var name: String = "中学生"
override def run(): String = name +"在奔跑!"
}
object PersonTest {
def main(args: Array[String]): Unit = {
val student = new SmallStudent
student.name = "小屁孩"
println(student.run())
}
}
带有具体实现的特质
注意:
scala的特质类中可以有具体实现的,类似于JDK8的接口
package traites
trait Animals {
// 注意:特质中既可以有抽象成员 也可以有具体成员
var name: String = "小动物"
def sleep(): Unit = {
println(name + ":在睡觉!")
}
def eat(): String
}
class Dog extends Animals {
override def eat(): String = "吃饭!"
override def sleep(): Unit = println(name +":sleeping")
}
object Dog {
def main(args: Array[String]): Unit = {
val dog = new Dog
dog.eat()
dog.sleep()
dog.name ="xx"
}
}
注意:让特质混有具体行为有一个弊端. 当特质改变时, 所有混入该特质的类都必须重新编译
带有特质的对象 动态混入(mixin) [ 重点]
动态混入(mixin):指在创建单个对象时,可以在不破换对象声明的前提下,扩展对象的功能
在构造单个对象时, 你可以为它添加特质
- 特质可以将对象原本没有的方法与字段加入对象中, 功能扩展
- 无需修改类声明定义,扩展类的功能,灵活简便
- 如果特质和对象改写了同一超类的方法, 则排在右边的先被执行
package traites.mixin
trait Animals {
println("animals")
def eat(): Unit
}
trait Person { // 都一个无参的主构造器
println("person")
def playGame(): Unit
}
trait Student extends Animals {
println("student")
// def study(): Unit
// 修改:
def study(): Unit
override def eat(): Unit = println("学生吃的肉和菜")
}
// 传统写法:在增强 SmallStudent 功能混入两个Trait
//class SmallStudent extends Person with Student {
// override def playGame(): Unit = ???
//
// override def study(): Unit = ???
//
// override def eat(): Unit = ???
//}
class SmallStudent extends Animals { // 在不破坏类的声明要求下,扩展功能(吃、玩游戏、学习能力)
println("smallStudent")
override def eat(): Unit = println("小学生吃零食")
}
object SmallStudent {
def main(args: Array[String]): Unit = {
// 注意:特殊语法,可以在创建伴生类时混入特质[抽象类],增强伴生类的功能,这样的语法就称为动态混入(mixin)
// 请问在有动态混入语法时:对象创建的顺序 从左到右进行初始化
/*
smallStudent
person
animals
student
*/
val smallStudent = new SmallStudent with Person with Student {
override def playGame(): Unit = println("打游戏")
override def study(): Unit = println("音乐和美术")
}
smallStudent.playGame()
smallStudent.study()
// 如果特质和对象改写了同一超类的方法, 则排在右边的先被执行
smallStudent.eat() // Person$eat 学生吃的肉和菜
}
}
this别名
给当前类(this)起一个别名(可以随便定制,建议
self
)
看scala的源码的话很发现很多源码开头都有一句:self =>
这句相当于给this
起了一个别名为self
。self
不是关键字,可以用除了this
外的任何名字命名(除关键字)。就下面的代码,在Student内部,可以用this
指代当前对象,也可以用self
指代,两者是等价的。
package traites
class AA {
// this起一个别名
self =>
var id: Int = 0
def getId(): Boolean = {
// self.id
this.id == self.id
}
}
object AA {
def main(args: Array[String]): Unit = {
val aa = new AA
val bool = aa.getId()
println(bool) //true
}
}
//-------------------------------------------------------
class Outer {
outer =>
val v1 = "here"
class Inner {
inner =>
val v1 = "-------------"
println(outer.v1) // 用outer表示外部类,相当于Outer.this
println(inner.v1)
}
}
object Outer {
def main(args: Array[String]): Unit = {
val outer = new Outer()
val inner = new outer.Inner
/**
* here
* -------------
*/
}
}
self-type(自类型)
强制混入
package traites
trait Cat {
val name: String
}
trait SmallCat{
// 自类型:强制混入,表示当前特质类必须混入类型Cat
self:Cat =>
def sayHi():String = {
self.name // 必须混入Cat
}
}
class SmallFamilyCat extends SmallCat with Cat{
override val name: String = "小花花"
}
object SmallFamilyCat{
def main(args: Array[String]): Unit = {
val cat = new SmallFamilyCat
val result = cat.sayHi()
println(result)
}
}
十三、高阶函数
匿名函数
略
传统写法:
def sum(x:Int,y:Int):Int = x+y
简化写法:
val f2:Function2[Int,Int,Int] = (x:Int,y:Int) => x+y
柯里化函数
略
传统写法:
def sum(x:Int) = (y:Int) => x+y
简化写法:
def sum(x:Int)(y:Int) = x+y
函数对象
Scala默认提供了23个函数对象,Function0~Function22
package function
object FunctionObject {
def main(args: Array[String]): Unit = {
val f2 = (x: Int, y: Int) => x + y
// 创建Function2函数对象
// val f2:Function2[Int,Int,Int] = new Function2[Int, Int, Int] {
// override def apply(v1: Int, v2: Int): Int = v1 + v2
// }
val sum = f2(1, 2)
println(sum)
}
}
函数对象作为参数
注意:
Scala的函数对象可以作为值进行传递,原因函数也是对象
package function
object HighLevelFunction01 {
def main(args: Array[String]): Unit = {
val f2 = (x: Int, y: Int) => x + y
sum(1,2,f2)
}
/**
*
* @param x
* @param y
* @param f2 函数对象
*/
def sum(x: Int, y: Int, f2: (Int, Int) => Int): Unit = {
println(f2(x,y)) // 3
}
}
参数(类型)推断
package function
object HighLevelFunction02 {
def main(args: Array[String]): Unit = {
val arr = Array(1, 2, 3, 4, 5)
// arr.foreach((n: Int) => println(n))
// 匿名函数 省略了参数列表的类型,因为匿名函数类型和数组元素类型一致
// arr.foreach((n) => println(n))
// 接受单个参数 () 省略
// arr.foreach(n => println(n))
// 接受单个参数 n => 省略
// arr.foreach(println(_))
arr.foreach(println)
}
}
一些有用的高阶函数
scala的函数实现wordcount
package function
object HighLevelFunction03ForWordCount {
def main(args: Array[String]): Unit = {
val lines = Array("Hello Hello","Hello Hadoop")
lines
.flatMap((line:String) => line.split(" ")) // 将一个内容展开为0~n个内容
.map((word:String) => (word, 1)) // 将一个类型转换为另外一个类型
.groupBy(t2 => t2._1) // 根据元组中的word分组
.map(t2 => (t2._1,t2._2.length))
.foreach(println)
println("-------------------------------")
lines
.flatMap(_.split(" "))
.map((_,1))
.groupBy(_._1)
.map(t2 => (t2._1,t2._2.length))
.filter(! _._1.equals("Hello")) //
.foreach(println)
/*
res4: scala.collection.immutable.Map[String,Array[(String, Int)]] =
Map(
Hadoop -> Array((Hadoop,1)),
Hello -> Array((Hello,1), (Hello,1), (Hello,1)))
*/
}
}
函数闭包
闭包指在函数体内,可以访问相应作用域内的任何变量,因为它引用到函数外面定义的变量,定义这个函数的过程是将这个自由变量捕获而构成一个封闭的函数。
语法:函数对象 + 外部变量 = 闭包
package function
object HighLevelFunction03 {
def main(args: Array[String]): Unit = {
var n = 0
// 函数闭包: 函数对象 + 外部变量 = 闭包
val f1 = (x: Int) => x + n
println(f1(1))
n = 100 // 函数对象闭包后,只保存一个外部变量的引用地址,当外部的变量在发生改变时,会影响到闭包后的结果
println(f1(2))
}
}
十四、异常处理
Java异常分类:
| - Throwable |- Error : 系统底层错误,无法解决 |- Exception(异常): |- RuntimeException |- 非 RuntimeException
Java 异常处理方法:
|- 消极处理 throw 抛出异常【动作】 throws 声明可能抛出异常【名词】 |- 积极处理 try...catch...finally...
注意:Scala异常处理类似于Java的异常处理
package exceptiones
/**
* 测试scala的异常处理
*/
object Exception01 {
def main(args: Array[String]): Unit = {
// m1()
m2()
println("-----------------------")
}
/**
* 消极处理 throw
*
* 1) scala保留了java异常处理类
* 2) scala不要通过throws声明可能抛出异常
*/
def m1(): Unit = {
// throw new RuntimeException
throw new NullPointerException
println("---------------")
}
/**
* 积极处理 try ... catch...finally
*/
def m2(): Unit = {
try {
var i: Int = 1 / 0
} catch {
// 模式匹配【详解】 匹配的try块中可能抛出异常的类型
case e1: NullPointerException => println("空指针异常")
case e2: RuntimeException => println("运行时异常")
case e3: Exception => println("异常!!!")
case _ => println("其它错误")
}
finally {
println("finally block")
}
}
}
十五、隐式转换【重点】
隐式转换函数(implicit conversion function)指的是以implicit关键字声明的带有单个参数的函数。这样的函数将被自动应用,将值从一种类型转换为另一种类型。隐式转换函数叫什么名字是无所谓的,因为通常不会由用户手动调用,而是由Scala进行调用。但是如果要使用隐式转换,则需要对隐式转换函数进行导入(import
)。因此通常建议将隐式转换函数的名称命名为“one2one”的形式。
常用使用方式:
- 隐式值
- 隐式参数
- 隐式函数
- 隐式增强
- 隐式类
Scala会考虑如下位置的隐式转换函数:
- 位于源或目标类型的伴生对象中的隐式函数
- 位于当前作用域可以以单个标识符指代的隐式函数
隐式转换在如下三种不同情况下会被考虑:【知道】
- 当表达式类型与预期类型不同时【隐式值的使用】
- 当对象访问一个不存在成员时【隐式增强】
- 当对象调用某个方法,而这个方法的参数声明与传入参数不匹配时【隐式函数】
有三种情况编译器不会尝试使用隐式转换: 【知道】
- 如果代码能够在不使用隐式转换的前提下通过编译,则不会使用隐式转换
- 编译器不会尝试同时执行多个转换
- 存在二义性的转换是错误
隐式值
在声明变量或者常量添加implicit关键字
如:
implicit var name:String = "zs"
package implicitconversion
/**
* 隐式值的使用
*/
object Implicit01 {
def main(args: Array[String]): Unit = {
// 1. 声明
implicit var name:String = "zs"
// implicit var address:String = "ls" //error
implicit val sex:Boolean = true //ok
// 2. 使用
// 注意:
// 1. 在使用隐式值时,通过隐式值的类型获取
// 2. 同类型的隐式值只能有一个
val newValue =implicitly[String]
val newValue2 =implicitly[Boolean]
println(newValue)
println(newValue2)
}
}
注意:
1. 在使用隐式值时,通过隐式值的类型获取
2. 同类型的隐式值只能有一个
隐式参数
有一些特殊的方法,含有隐式参数
语法:
def sayHi(implicit name:String):Unit = {}
注意: 如果方法或者函数含有隐式参数,会自动在作用域寻找隐式值赋予隐式参数
package implicitconversion
object Implicit02 {
def main(args: Array[String]): Unit = {
implicit val name: String = "zs"
implicit val default: Int = 0
sayHi // 触发隐式参数的匹配
sayHi("ls") // 因为调用时有具体的参数值,并不会触发隐式参数的匹配
println(sum(1)(2)) // 3
println(sum(1)) // 1
println(sum2) // 0 zs
println(sum2(10,"ls")) // 10 ls
}
/**
*
* @param name 隐式参数:在作用域中寻找同类型隐式值 同类型隐式值赋予隐式参数
*/
def sayHi(implicit name: String): Unit = {
println("Hello:" + name)
}
def sum(x: Int)(implicit y: Int): Int = {
x + y
}
def sum2(implicit x: Int, y: String): String = { // x和y都为隐式参数
x + "\t" + y
}
}
隐式函数
将一种类型自动转换为另外一种类型
package implicitconversion
object Implicit03 {
def main(args: Array[String]): Unit = {
sayHi(new Person("zs")) // 正常写法
sayHi("ls")
}
/**
* 隐式函数: 将一种类型转换为另外一种类型
*
* 注意:隐式函数不是我们调用的,是scala在编译不通过式,会在作用域寻找可以进行转换函数
* @param
*/
implicit def str2Person(str:String):Person={
new Person(str)
}
/*
implicit def int2String(n:Int) :String = {
n+""
}
*/
def sayHi(p: Person): Unit = {
println("Hello:" + p.name)
}
}
class Person(var name: String) {
}
隐式增强
通过隐式转换增强某一个类的功能
package implicitconversion
/**
* 隐式增强
*/
object Implicit04 {
def main(args: Array[String]): Unit = {
val man = new Man("zs")
man.fly
}
// man ---> superMan
implicit def man2SuperMan(man:Man):SuperMan = {
new SuperMan(man.name)
}
}
class Man(var name:String)
class SuperMan(var name:String){
def fly={
println("fly~~~~~~~")
}
}
隐式类
implicit修饰的特殊类,注意不能独立声明,需要定义在一个类\对象的内部
package implicitconversion
/**
* 隐式类
*/
object Implicit05 {
def main(args: Array[String]): Unit = {
val man = new Man2("zs")
man.fly
// 隐式类: 主构造器接受一个类型 转换为隐式类类型 man2 ---> SuperMan2
implicit class SuperMan2(man2: Man2) {
var name: String = ""
def fly = {
this.name = man2.name
println("fly~~~~~~~~")
}
}
}
}
class Man2(var name:String)
隐式转换的导入
总结:
- 如果在当前作用域(源文件)有隐式转换,无需导入
- 如果在当前作用域没有隐式转换,需要手动导入,使用语法
import xxx
package implicitconversion
/**
* 隐式转换的使用:
* - 对象中的隐式转换,使用如下的语法 对象名._
* - 类中隐式转换,使用如下的语法: val 引用 = new 类
* import 引用._ 【语法】
*
* 注意:
* 导入的隐式转换不能和当前类的成员产生命名冲突
*/
object Implicit06 {
def main(args: Array[String]): Unit = {
// 对象中的隐式转换,使用如下的语法
import AA._
val n2: Int = implicitly[Int]
println(n2)
val s: Boolean = implicitly[Boolean]
println(s)
// 类中隐式转换,使用如下的语法:
val bb = new BB
import bb._
val str2: String = implicitly[String]
println(str2)
val c2: Char = implicitly[Char]
println(c2)
}
}
object AA {
implicit val n: Int = 10
implicit val sex: Boolean = false
}
class BB {
implicit val str: String = "bb"
implicit val c: Char = 'A'
}
十六、泛型
泛型规定了类或者方法的使用类型
泛型类
语法:
类名[泛型类型,....]
package generic
/**
* 泛型类
* T S M K
*/
class Generic01[T, S, K](var name: T, var sex: S) {
var age: K = _
override def toString: String = name + "\t" + sex + "\t" + age
}
object Generic01 {
def main(args: Array[String]): Unit = {
val g1 = new Generic01[String, Boolean, Int]("zs", false)
g1.age = 10
println(g1)
}
}
泛型方法或函数
package generic
/**
* 泛型方法或者函数
*/
object Generic02 {
def main(args: Array[String]): Unit = {
print[Int](Array(1, 2, 3, 4, 5))
// print[Int](Array("a","b","c")) //error
}
def print[T](arr: Array[T]): Unit = {
arr.foreach(println)
}
}
上边界
语法:
[T <: S]
, 限定T类型必须是S的子类型 ,包含S类型本身
package generic.upper
class A
class B extends A
// 上边界 T类型必须A子类型
class C[T <: A](var t: T)
class D
object C{
def main(args: Array[String]): Unit = {
val c1 = new C[A](new A) //A类型本身 ok
val c2 = new C[B](new B) //A类型的子类B ok
// val c3 = new C[D](new D) //A类型的子类B ERROR
}
}
下边界
语法:
[T >: S]
,限定T类型必须S的父类型,包含S类型本身
package generic.lower
class A
class B extends A
// 下边界
class C[T >: B](var t: T)
class D extends B
object C {
def main(args: Array[String]): Unit = {
val c1 = new C[B](new B) // 下边界本身类型 ok
val c2 = new C[A](new A) // A类型是B类型 ok
val c3 = new C[D](new D) // D类型是B类型子类型 不符合下边界语义 ERROR
}
}
视图边界
语法:
[T <% S]
, 运行时尝试将T类型隐式转换为S类型
package generic.viewbound
import java.text.SimpleDateFormat
/**
* 视图边界【限定】
*
* str ---> java.util.Date
*/
class A[T <% java.util.Date](val date: T)
object A {
def main(args: Array[String]): Unit = {
val a = new A[String]("2019-11-20 15:21:30")
println(a.date.getYear +"\t"+a.date.getMonth + "\t" +a.date.getDay)
}
// 提供一个隐式转换函数 str --> java.util.Date
implicit def str2Date(str: String): java.util.Date = {
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(str)
}
}
多重限定
多上界
语法:
[T <: S with K]
表示T类型必须同时是S类型和K类型的子类型
package generic.multiupper
class A
class B extends A
class C extends B
// 多上界 表示:T 类型必须是B类型和A类型的子类型
// 特点:包含最小的上边界B类型、B类型和A类型的子类
class D[T <: B with A](t:T)
class E
object D{
def main(args: Array[String]): Unit = {
val d1 = new D[C](new C) // OK
val d2= new D[B](new B) // OK
// val d3= new D[A](new A) // ERROR
// val d4= new D[E](new E) // ERROR
}
}
多下界
语法:
[T >: S with K]
表示T类型是S类型或者K类型的父类型
package generic.multilower
class A
class B extends A
class C
class D extends C
// 多下界 表示 T类型必须是B类型或者D类型的父类型
class E[T >: B with D](t: T)
// class F
class F extends D
object E{
def main(args: Array[String]): Unit = {
val e1 = new E[A](new A) //ok
val e2 = new E[D](new D) //ok
val e3 = new E[B](new B) //ok
val e4 = new E[C](new C) //ok
//val e5 = new E[F](new F) //error
}
}
既有上界又有下界
语法:
[T >: S <: K]
表示T类型的上边界为K类型,下边界为S类型
package generic.upper_lower
class A
class B extends A
class C extends B
// 既有上边界 又下边界
class D[T >: C <: A](var t: T)
class E extends C
object D{
def main(args: Array[String]): Unit = {
val d1 = new D[B](new B) //ok
val d2 = new D[A](new A) //ok
val d3 = new D[C](new C) //ok
// val d4 = new D[E](new E) //error
}
}
协变
语法:
[+T]
如果T类型是T1类型的父类型,则 C[T]类型是C[T1]类型的父类
class A
class B extends A
class C[+T]
C[A] ---> C[B] 父类
//---------------------------------------------------------
package generic.xb
class A
class B extends A
class C[+T]
object C {
def main(args: Array[String]): Unit = {
val c1 = new C[B]
// 协变后 C[A] 依然是 C[B] 父类型
val c2: C[A] = c1
}
}
逆变
语法:
[-T]
如果T类型是T1类型的父类型,则 C[T1]类型是C[T]类型的父类
class A
class B extends A
class C[-T]
C[B] ---> C[A] 父类
//-------------------------------------------------------
package generic.nb
class A
class B extends A
// 逆变
class C[-T]
object C{
def main(args: Array[String]): Unit = {
val c1 = new C[A]
val c2:C[B] = c1
}
}
不变
语法:
[T]
,使用方法同泛型类和泛型方法
上下文限定
语法:
[T:M]
上下文界定的形式为T:M,其中M是另一个泛型类,它要求必须存在一个类型为M[T]的隐式值。
package generic.context
// 语法:`[T:M]`
//
// 上下文界定的形式为T:M,其中M是另一个泛型类,它要求必须存在一个类型为M[T]的隐式值。
class Person(var name: String, var age: Int)
/**
* T: 泛型类(Ordering)
*
* @tparam T
*/
class CompareUtils[T: Ordering](p1: T, p2: T) {
def compare: Int = {
val ordering = implicitly[Ordering[T]] // 获取一个Ordering[T]隐式值
ordering.compare(p1,p2)
}
}
object Person {
// 声明ordering[Person]的隐式值【对象】
implicit val ordering: Ordering[Person] = new Ordering[Person] {
override def compare(x: Person, y: Person): Int = if (x.age > y.age) 1 else -1
}
def main(args: Array[String]): Unit = {
val p1 = new Person("zs", 20)
val p2 = new Person("ls", 30)
val tools = new CompareUtils[Person](p1, p2)
if(tools.compare == 1){
println("大于")
}else{
println("小于")
}
}
}
十七、集合
Scala提供了一套很好的集合实现,提供了一些集合类型的抽象。
Scala 集合分为可变的和不可变的集合:
- 可变集合(
mutable
) 可以在适当的地方被更新或扩展。这意味着你可以修改,添加,移除一个集合的元素。 - 不可变集合(默认
immutable
)类永远不会改变。不过,你仍然可以模拟添加,移除或更新操作。但是这些操作将在每一种情况下都返回一个新的集合,同时使原来的集合不发生改变。
集合中基本结构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c8AluOGL-1575721948444)(assets/20160408203057811)]
Java中集合结构:
|- Iterable
|- Collection
|- List 有序 有小标 元素可重复
|- ArrayList
|- Vector
|- LinkedList
|- Set 无序 无下标 元素不可重复
|- HashSet
|- TreeSet
|- LinkedHashSet
|- Map K-V键值对
|- HashMap
|- HashTable
|- TreeMap
|- Properties
Seq 序列
Ranage
数值范围区间
scala> 1 to 10
res0: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8,
9, 10)
scala> 1 until 10
res2: scala.collection.immutable.Range = Range(1, 2, 3, 4, 5, 6, 7, 8, 9)
scala> 1 until 10 by 2
res3: scala.collection.immutable.Range = Range(1, 3, 5, 7, 9)
scala> new Range(1,10,2)
res4: scala.collection.immutable.Range = Range(1, 3, 5, 7, 9)
scala> Range(1,10,2)
res6: scala.collection.immutable.Range = Range(1, 3, 5, 7, 9)
Vector(向量集合)
树形结构实现,查找效率极高
scala> Vector(1,2,3,4,5)
res7: scala.collection.immutable.Vector[Int] = Vector(1, 2, 3, 4, 5)
scala> Vector[String]("A","B","C")
res9: scala.collection.immutable.Vector[String] = Vector(A, B, C)
scala> for(n <- 1 to 10 if n % 2 == 0) yield n
res10: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8, 10)
List (不可变的列表)
immutable
不可变的列表
// 声明
scala> List(1,2,3,4,5)
res11: List[Int] = List(1, 2, 3, 4, 5)
// 获取元素 下标
scala> res11(2)
res12: Int = 3
// 是否可以对不可变列表中的元素进行更改
scala> res11(2) = 13
<console>:13: error: value update is not a member of List[Int]
res11(2) = 13
^
// 支持增删改操作,在新集合的基础之上完成的
// 在集合的尾部添加元素
scala> res11 :+ 6
res15: List[Int] = List(1, 2, 3, 4, 5, 6)
scala> res11 == res15
res16: Boolean = false
scala> res11
res17: List[Int] = List(1, 2, 3, 4, 5)
// 将0添加到集合的头部
scala> 0 +: res11
res19: List[Int] = List(0, 1, 2, 3, 4, 5)
// 移除元素
scala> res25.drop(1)
res27: List[Int] = List(2, 3)
scala> res25 == res27
res28: Boolean = false
Nil
空列表,没有任何元素的List集合
scala> val list = Nil
list: scala.collection.immutable.Nil.type = List()
::
和 :::
::
如:A :: List
将A作为整体添加到List集合的头部
:::
如:A ::: List
将A中的各个元素添加到List集合的头部
scala> 1 :: Nil
res20: List[Int] = List(1)
scala> 2:: res20
res21: List[Int] = List(2, 1)
scala> 3::res21
res22: List[Int] = List(3, 2, 1)
scala> List(1,2,3) :: res22
res23: List[Any] = List(List(1, 2, 3), 3, 2, 1)
scala> List(1,2,3) ::: res22
res24: List[Int] = List(1, 2, 3, 3, 2, 1)
ListBuffer(可变列表)
mutable
可以在原始集合的基础之上,进行改操作
// 声明一
scala> scala.collection.mutable.ListBuffer(1,2,3,4,5)
res29: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3, 4, 5)
// 声明二
scala> import scala.collection.mutable._ // 等价于 import scala.collection.mutable.*
scala> ListBuffer("A","B")
res30: scala.collection.mutable.ListBuffer[String] = ListBuffer(A, B)
// 使用
// 获取
scala> res1(1)
res2: String = B
// 修改
scala> res1(1) = "C"
scala> res1
res4: scala.collection.mutable.ListBuffer[String] = ListBuffer(A, C)
// 相同的对象
scala> res4 == res1
res5: Boolean = true
// 添加
scala> res1 += "D"
res6: res1.type = ListBuffer(A, C, D)
scala> res1.+=("E")
res7: res1.type = ListBuffer(A, C, D, E)
// 移除
scala> res1 -= "A"
res9: res1.type = ListBuffer(C, D, E)
// ++= 将后一个集合中的元素追加到当前集合的末尾
scala> res1 ++= ListBuffer("X","Y","Z")
res13: res1.type = ListBuffer(D, E, C, X, Y, Z)
// A +=:ListBuffer 表示将A添加到ListBuffer头部
scala> "o" +=: res1
res14: res1.type = ListBuffer(o, D, E, C, X, Y, Z)
结论:
- 通常情况下不可变集合的操作符是不含
=
,操作完成后返回一个新集合- 通常情况下可变集合的操作符是含有
=
,操作时在当前集合完成的,不会返回新集合了解更多操作符的知识:
请关注: 资料13.7章节
遍历
scala> res1.foreach( println(_)) // “_“ 代表的是集合中的每一个元素
o
D
E
C
X
Y
Z
scala> for(n <- res1){println(n)}
o
D
E
C
X
Y
Z
scala> for(n <- 0 until res1.size){println(res1(n))}
o
D
E
C
X
Y
Z
Set
Set 不可变(immutable)
// 声明
scala> scala.collection.immutable.Set[Int](1,2,3,2)
res24: scala.collection.immutable.Set[Int] = Set(1, 2, 3)
// 获取
scala> res24.foreach(println)
1
2
3
// 改操作
scala> res24 + 4
res26: scala.collection.immutable.Set[Int] = Set(1, 2, 3, 4)
scala> res24 + 3
res27: scala.collection.immutable.Set[Int] = Set(1, 2, 3)
scala> res24 == res26
res28: Boolean = false
scala> res24 - 2
res29: scala.collection.immutable.Set[Int] = Set(1, 3)
Set可变集合(mutable)
scala> scala.collection.mutable.Set[Int](1,2,3,3)
res31: scala.collection.mutable.Set[Int] = Set(1, 2, 3)
// 操作
scala> res31 += 4
res32: res31.type = Set(1, 2, 3, 4)
scala> res32 == res31
res33: Boolean = true
scala> res31 -= 2
res34: res31.type = Set(1, 3, 4)
// 遍历方法(略)
HastSet(略)
TreeSet(略)
Map
元素 k,v键值对
可变和不可变
// 方法一
// 可变
scala> Map("bj"->"北京","tj"->"天津")
res35: scala.collection.mutable.Map[String,String] = Map(tj -> 天津, bj -> 北京)
// 不可变
scala> scala.collection.immutable.Map[String,String]("bj"->"北京","tj"->"天津")
res36: scala.collection.immutable.Map[String,String] = Map(bj -> 北京, tj -> 天津)
// 方法二
scala> Map(("bj","北京"),("tj","天津"))
res37: scala.collection.mutable.Map[String,String] = Map(tj -> 天津, bj -> 北京)
scala.collection.immutable.Map(("bj","北京"),("tj","天津"))
// 操作
// 根据k取V值
scala> res0("bj")
res1: String = 北京
// 根据k修改v(可变集合)
scala> res3("bj")="bjj"
scala> res3
res5: scala.collection.mutable.Map[String,String] = Map(tj -> 天津, bj -> bjj)
//添加k v对
scala> res3.+=(("sh","上海"))
res6: res3.type = Map(tj -> 天津, bj -> bjj, sh -> 上海)
//根据k移除kv
scala> res3.remove("tj")
res7: Option[String] = Some(天津)
scala> res3
res8: scala.collection.mutable.Map[String,String] = Map(bj -> bjj, sh -> 上海)
// 通过声明Tuple1元组 删除kv
scala> res3 -= (("sh"))
res9: res3.type = Map(bj -> bjj)
scala> res3
res10: scala.collection.mutable.Map[String,String] = Map(bj -> bjj)
// 遍历
scala> res3.foreach(t => println(t._1 +"\t"+t._2))
bj bjj
// 键遍历
scala> res13.foreach(k =>println(res3(k)))
bjj
// 值遍历
scala> res3.values
res15: Iterable[String] = HashMap(bjj)
Tuple(元组)【重点】
一种特殊的数据类型,可以存放任意的值,类似于
ArrayList[Object]
scala中内置了22个元组
Tupe1~Tuple22
通常使用Tuple2,描述Map集合的KV键值对
// 声明
scala> ("zs",1)
res18: (String, Int) = (zs,1)
scala> res18.getClass
res19: Class[_ <: (String, Int)] = class scala.Tuple2
scala> ("zs",1,false)
res20: (String, Int, Boolean) = (zs,1,false)
scala> res20.getClass
res21: Class[_ <: (String, Int, Boolean)] = class scala.Tuple3
// 获取元组中的值 语法: 元组引用名._n 获取元组中第n个值
scala> res20._1
res22: String = zs
scala> res20._2
res23: Int = 1
// 元组的遍历
scala> res20.productIterator
res24: Iterator[Any] = non-empty iterator
scala> for(n <- res24) println(n)
zs
1
false
Java和Scala集合的互操作
package collection
object JavaAndScalaCollectionOpt {
def main(args: Array[String]): Unit = {
// 导入隐式转换
import scala.collection.JavaConverters._
// 1. 创建Java集合
val list = new java.util.ArrayList[Int]()
list.add(1)
list.add(2)
list.add(3)
// 2. java集合转化scala的集合
val scalaList = list.asScala
val newScalaList = scalaList :+ 4
newScalaList.foreach(println)
// 3. scala集合转换java集合
val converterJavaList = newScalaList.asJava
println(converterJavaList.get(3))
}
}
十八、模式匹配和样例类
match pattern
模式匹配是Scala中非常有特色,非常强大的一种功能。模式匹配,其实类似于Java中的swich case语法,即对一个值进行条件判断,然后针对不同的条件,进行不同的处理。
但是Scala的模式匹配的功能比Java的swich case语法的功能要强大地多,Java的swich case语法只能对值进行匹配。但是Scala的模式匹配除了可以对值进行匹配之外,还可以对类型进行匹配、对Array和List的元素情况进行匹配、对case class进行匹配、甚至对有值或没值(Option)进行匹配。
值匹配
package matchpattern
/**
* 值匹配
*/
object MatchPattern01 {
def main(args: Array[String]): Unit = {
val str:String = "C"
// 传入的值和case的值进行精确匹配,和Java switch case同理,不存在穿透问题
str match{
case "A" => println("A")
case "B" => println("B")
case _ => println("其它") // `_` 表示default 其它
}
}
}
if守卫
if 判断
/**
* 值匹配
*/
object MatchPattern02 {
def main(args: Array[String]): Unit = {
// val age:Int = 20
val age:Int = 45 // 类似于: if... else if... else
// 传入的值和case的值进行精确匹配,和Java switch case同理,不存在穿透问题
// if守卫
age match{
case _ if(age > 60) => println("老年人") // `_` 匹配任意值
case _ if(age > 40) => println("中年人")
case _ if(age >= 20) => println("青年人")
case _ => println("少年&儿童") // `_` 表示default 其它
}
}
}
模式匹配中的变量
package matchpattern
/**
* 变量
*/
object MatchPattern03 {
def main(args: Array[String]): Unit = {
// val age:Int = 20
val age: Int = 18 // 类似于: if... else if... else
// 传入的值和case的值进行精确匹配,和Java switch case同理,不存在穿透问题
// if守卫
age match {
case a1 if (a1 > 60) => println("老年人:" + a1) // `_` 匹配任意值
case a2 if (a2 > 40) => println("中年人:" + a2)
case a3 if (a3 >= 20) => println("青年人:" + a3)
case _default => println("少年&儿童:" + _default) // `_` 表示default 其它
}
}
}
类型匹配
对表达式的类型进行匹配,Scala推荐使用这样的方式进行类型匹配,而不是使用
isInstanceOf
操作
package matchpattern
/**
* 类型匹配
*/
object MatchPattern04 {
def main(args: Array[String]): Unit = {
// val t: AnyVal = 10.0
// val t: AnyVal = 10
// val t: AnyVal = true
val t: Any = "aa"
// 传入的值和case的值进行精确匹配,和Java switch case同理,不存在穿透问题
// if守卫
t match {
case d: Double => println("小数:" + d)
case n: Int => println("整数:" + n)
case b: Boolean => println("布尔:" + b)
case _o => println("其它类型:" + _o)
}
}
}
集合元素匹配
个数,类型,内容
package matchpattern
/**
* 集合匹配
*/
object MatchPattern05 {
def main(args: Array[String]): Unit = {
// val arr = Array(1,2,3,4,5)
// val arr = Array("A","B")
// val arr = Array('a','b','c','d')
// val arr: Array[Any] = Array('a')
// 匹配元素的个数【匹配数组中包含5个元素】
/*
arr match {
case Array(n1,n2) => println("匹配2个元素的集合")
case Array(n1,n2,n3,n4,n5) => println("匹配5个元素的集合")
case Array(n1,n2,n3,_*) => println("匹配至少3个元素的集合")
case _ => println("匹配其它个数元素的集合")
}
*/
// val arr:Array[Any] = Array(1,2,3,4,5)
// val arr = Array("A","B")
// val arr = Array('a','b','c','d')
// val arr: Array[Any] = Array('a')
// 匹配元素的内容
/*
arr match {
case Array('a') => println("字符 ‘a’")
case Array(1, 2, 3, 4, 5) => println("数值 1,2,3,4,5")
case Array("a", "b") => println("字符串 a&b'")
case _ => println("其它")
}
*/
// 集合元素内容类型匹配【只适用于Array类型】
// val t = (1, "zs", true)
// val t = (1, "zs", true,'A')
/*
val t = (1, "zs", true, 'A', 10.0)
t match {
case t3: (Int, String, Boolean) => println("tuple3[Int,String,Boolean]")
case t4: Tuple4[Int, String, Boolean, Char] => println("tuple4[Int,String,Boolean,Char]")
case (_n1: Int, _n2: String, _n3: Boolean, _n4: Char, _n5: Double) => println("tuple5[Int,String,Boolean,Char,Double]")
case _ => println("other")
}
*/
val list = List[Int](1,2,3)
// scala编译器 编译完成后会抹掉集合类型的泛型信息,这样的话在运行时是无泛型的一种匹配,谁在最前面谁优先被执行
list match {
case l1:List[String] => println("list[String]")
case l2:List[Int] => println("list[Int]")
case _ => println("other")
}
}
}
Case Class(样例类)
-
Scala中提供了一种特殊的类,用case class进行声明,中文也可以称作样例类。case class其实有点类似于Java中的JavaBean的概念。即只定义field,并且由Scala编译时自动提供getter方法,但是没有method。
-
case class的主构造函数接收的参数通常不需要使用var或val修饰,Scala自动就会使用val修饰(但是如果你自己使用var修饰,那么还是会按照var来)
-
Scala自动为case class定义了伴生对象,也就是object,并且定义了apply()和unapply()方法,该方法接收主构造函数中相同的参数,并返回case class对象
-
case class常用于模式匹配
package matchpattern /** * 样例类 */ object CaseClass01 { def main(args: Array[String]): Unit = { val student = new Student("zs",18) student.age student.age = 10 student.name // student.name = "aa" // ERROR val student2 = Student("zs",20) } } // Student 样例类 val name,只有getter没有setter // var age, 有getter和setter case class Student(name: String, var age: Int)
package matchpattern /** * 注意: * scala的样例类 通常用于模式匹配,将对象的值拆解出来 */ object CaseClass02 { def main(args: Array[String]): Unit = { // val p: Person = Student2("zs",18,"101") // val p: Person = Teacher2("zs",18,"101") val p: Person = NoBody2("zs") p match { // unapply方法 将对象拆分为值 case Student2(name, age, stuNo) => println(name + "\t" + age + "\t" + stuNo) case Teacher2(name, age, stuNo) => println(name + "\t" + age + "\t" + stuNo) case NoBody2(name) => println(name ) case _other => println(_other) } } } // 抽象类 abstract class Person case class Student2(name: String, age: Int, stuNo: String) extends Person case class Teacher2(name: String, age: Int, teaNo: String) extends Person case class NoBody2(name: String) extends Person
sealed(密封类)
密封类的所有子类都必须在与该密封类相同的文件中定义,主要作用:
- 防止滥用继承:sealed关键字可以修饰类和特质(特质)**。密封类提供了一种约束:**不能在类定义的文件之外定义任何新的子类。如:scala的List的实现使用了sealed关键字
- 语法校验
package matchpattern
sealed class A
// B类和A类在相同的源文件中(OK)
class B extends A
tern05 {
def main(args: Array[String]): Unit = {
// val arr = Array(1,2,3,4,5)
// val arr = Array(“A”,“B”)
// val arr = Array(‘a’,‘b’,‘c’,‘d’)
// val arr: Array[Any] = Array(‘a’)
// 匹配元素的个数【匹配数组中包含5个元素】
/*
arr match {
case Array(n1,n2) => println("匹配2个元素的集合")
case Array(n1,n2,n3,n4,n5) => println("匹配5个元素的集合")
case Array(n1,n2,n3,_*) => println("匹配至少3个元素的集合")
case _ => println("匹配其它个数元素的集合")
}
*/
// val arr:Array[Any] = Array(1,2,3,4,5)
// val arr = Array("A","B")
// val arr = Array('a','b','c','d')
// val arr: Array[Any] = Array('a')
// 匹配元素的内容
/*
arr match {
case Array('a') => println("字符 ‘a’")
case Array(1, 2, 3, 4, 5) => println("数值 1,2,3,4,5")
case Array("a", "b") => println("字符串 a&b'")
case _ => println("其它")
}
*/
// 集合元素内容类型匹配【只适用于Array类型】
// val t = (1, "zs", true)
// val t = (1, "zs", true,'A')
/*
val t = (1, "zs", true, 'A', 10.0)
t match {
case t3: (Int, String, Boolean) => println("tuple3[Int,String,Boolean]")
case t4: Tuple4[Int, String, Boolean, Char] => println("tuple4[Int,String,Boolean,Char]")
case (_n1: Int, _n2: String, _n3: Boolean, _n4: Char, _n5: Double) => println("tuple5[Int,String,Boolean,Char,Double]")
case _ => println("other")
}
*/
val list = List[Int](1,2,3)
// scala编译器 编译完成后会抹掉集合类型的泛型信息,这样的话在运行时是无泛型的一种匹配,谁在最前面谁优先被执行
list match {
case l1:List[String] => println("list[String]")
case l2:List[Int] => println("list[Int]")
case _ => println("other")
}
}
}
### Case Class(样例类)
- Scala中提供了一种特殊的类,用case class进行声明,中文也可以称作样例类。case class其实有点类似于Java中的JavaBean的概念。即只定义field,并且由Scala编译时自动提供getter方法,但是没有method。
- case class的主构造函数接收的参数通常不需要使用var或val修饰,Scala自动就会使用val修饰(但是如果你自己使用var修饰,那么还是会按照var来)
- Scala自动为case class定义了伴生对象,也就是object,并且定义了apply()和unapply()方法,该方法接收主构造函数中相同的参数,并返回case class对象
- case class常用于模式匹配
```scala
package matchpattern
/**
* 样例类
*/
object CaseClass01 {
def main(args: Array[String]): Unit = {
val student = new Student("zs",18)
student.age
student.age = 10
student.name
// student.name = "aa" // ERROR
val student2 = Student("zs",20)
}
}
// Student 样例类 val name,只有getter没有setter
// var age, 有getter和setter
case class Student(name: String, var age: Int)
package matchpattern
/**
* 注意:
* scala的样例类 通常用于模式匹配,将对象的值拆解出来
*/
object CaseClass02 {
def main(args: Array[String]): Unit = {
// val p: Person = Student2("zs",18,"101")
// val p: Person = Teacher2("zs",18,"101")
val p: Person = NoBody2("zs")
p match {
// unapply方法 将对象拆分为值
case Student2(name, age, stuNo) => println(name + "\t" + age + "\t" + stuNo)
case Teacher2(name, age, stuNo) => println(name + "\t" + age + "\t" + stuNo)
case NoBody2(name) => println(name )
case _other => println(_other)
}
}
}
// 抽象类
abstract class Person
case class Student2(name: String, age: Int, stuNo: String) extends Person
case class Teacher2(name: String, age: Int, teaNo: String) extends Person
case class NoBody2(name: String) extends Person
sealed(密封类)
密封类的所有子类都必须在与该密封类相同的文件中定义,主要作用:
- 防止滥用继承:sealed关键字可以修饰类和特质(特质)**。密封类提供了一种约束:**不能在类定义的文件之外定义任何新的子类。如:scala的List的实现使用了sealed关键字
- 语法校验
package matchpattern
sealed class A
// B类和A类在相同的源文件中(OK)
class B extends A