Scala Language
Scala语言
Scala是一门多范式的编程语言,能够以一种优雅的方式来解决现实问题,同时支持面向对象和面向函数的编程风格。虽然它是强静态类型的编程语言,但是它强大的类型推断能力,使其看起来就像是一个动态编程语言。Scala语言最终会被编译成java字节码文件,可以和JVM无缝集成,并且可以使用Scala调用Java的代码库。Scala编程语言除了自身的特性以外,目前比较流行的Spark计算框架也是使用Scala语言编写。Spark 和 Scala 能够紧密集成,例如,使用Scala语言操作大数据集合时,用户可以像是在操作本地数据集那样简单操作Spark上的分布式数据集-RDD(这个概念是Spark 批处理的核心术语),继而简化大数据集的处理难度,简化开发步骤。
特点
- 面向对象编程语言 传递数据 | 数据建模
- 面向函数式编程语言 专门为数据而生的语言 传递函数 | 数据分析计算
- 强大的集合计算能力 (后续课程介绍)
因为现在主流大数据计算框架均对Scala编程支持比较友好,例如:Kafka.0.11.0_2.11.tar.gz、Spark使用Scala,导致使用Scala语言可以高效实现数据分析处理。
编程指南:https://docs.scala-lang.org/tour/tour-of-scala.html
环境安装
下载对应的Scala版本:https://www.scala-lang.org/download/2.11.12.html
Windows版本安装
- 点击
scala-2.11.12.msi
双击msi文件安装 - 配置Scala的环境变量SCALA_HOME变量
SCALA_HOME=C:\Program Files (x86)\scala
PATH=C:\Program Files\Java\jdk1.8.0_161/bin;C:\Program Files (x86)\scala/bin;
- 打开Window窗口
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>
CentOS安装
- 下载
scala-2.11.12.rpm
- 安装配置Scala
[root@CentOS ~]# rpm -ivh scala-2.11.12.rpm
Preparing... ########################################### [100%]
1:scala ########################################### [100%]
[root@CentOS ~]# scala
Welcome to Scala 2.11.12 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_171).
Type in expressions for evaluation. Or try :help.
scala>
IDEA集成Scala开发环境
在File>Setting>Plugins点击 install pluginfromdisk
选项,选择 scala-intellij-bin-2018.2.11.zip
安装成功后,重启IDEA
Scala变量
Scala语言没有基本数据类型,这一点和Java语言不同,在Scala中一切皆对象,以下是Scala语言中的常见类型和类型间的继承关系:
Scala是一门纯粹的面向对象编程语言,该语言没有基本类型。所有值类型都是AnyVal的子类,所有对象类型都是AnyRef的子类。一般所有的AnyVal类型都有字面值,所有AnyRef类型的默认值都是Null
变量定义
Scala语言是一种可以做类型自动推断的强类型的编程语言。变量的类型可以通过编译器在编译时自动推断出最终类型,因此在Scala中声明一个变量,需要告知编译器该变量的值是常量还是变量,例如:声明一个变量使用var关键字,声明一个常量使用val关键字。Scala语言的变量声明语法如下:
[var|val] 变量名[:变量类型]=变量值[:类型]
scala> var i:Int=1:Int
i: Int = 1
scala> var i=1:Int
i: Int = 1
scala> var i=1
i: Int = 1
scala> var j=1
j: Int = 1
scala> var j=1:Byte
j: Byte = 1
scala> var j:Byte=1
j: Byte = 1
由于Scala语言具有强大的类型推断能力,因此在声明变量的时候,如果类型不存在歧义,一般可以省略
asInstanceOf
当类型兼容时,使用asInstanceOf[目标类型]强转,例如:Long转换Int、Double转换为Float,大->小
类型兼容
scala> var j:Byte=1
j: Byte = 1
scala> var i=10
i: Int = 10
scala> var j:Byte=0
j: Byte = 0
scala> j=i
<console>:13: error: type mismatch;
found : Int
required: Byte
scala> i=j
i: Int = 0
scala> j=i.asInstanceOf[Byte]
j: Byte = 0
j=i
^
toXxx类型
将字符串的字面值尝试转换为值类型
scala> var i=0
i: Int = 0
scala> var j="123"
j: String = 123
scala> i=j
<console>:13: error: type mismatch;
found : String
required: Int
i=j
^
scala> i=j.asInstanceOf[Int]
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integ
er
at scala.runtime.BoxesRunTime.unboxToInt(BoxesRunTime.java:101)
scala> i=j.toInt
i: Int = 123
... 32 elided
元组
由多个元素组成的复合变量称为元组,元组的值都是只读的,元组的元素最多不可以超过22个
scala> var t=(1,"zs",false)
t: (Int, String, Boolean) = (1,zs,false)
scala> t._2
res0: String = zs
scala> t._2="lisi"
<console>:12: error: reassignment to val
t._2="lisi"
^
数组
scala> var a=new Array[Int](4)
a: Array[Int] = Array(0, 0, 0, 0)
scala> var a=Array(1,2,3,4)
a: Array[Int] = Array(1, 2, 3, 4)
scala> a(0)= -1
scala> a
res4: Array[Int] = Array(-1, 2, 3, 4)
scala> a.length
res5: Int = 4
scala> a.size
res6: Int = 4
Unit
Unit是一种特殊的值类型,该类型是一个空的元组,通常作为函数的返回值,等价于Java中的void关键字
scala> var u:Unit=()
u: Unit = ()
分支循环
if条件分支
语法
if(条件){
}else if(条件){
}
...
else{
}
var a=28
if(a<=10){
println("child")
}else if(a<=20){
println("boy|girl")
}else{
println("man|woman")
}
和Java不同,if 可以将代码块的返回值返回给一个变量
var a=28
var result=if(a<=10){
"child"
}else if(a<=20){
"boy|girl"
}else{
"man|woman"
}
println(s"结果:${result}")
while、do-while
while(条件){
//循环体
}
在scala语言中while和do-while没有continue和break关键字
var a=5
while (a>0){
print("\t"+a)
a -= 1
}
---
do{
print("\t"+a)
a -= 1
}while(a>0)
Breaks
Scala 语言中默认没有 break 语句,但是在 Scala 2.8 版本以后可以使用另外一种方式来实现 break 语句功能。在循环中使用 break.break() 语句,当程序执行到该语句时,就会中断循环并执行循环体之后的代码块
scala> import scala.util.control.Breaks
import scala.util.control.Breaks
scala> var a=10
a: Int = 10
scala> var break=new Breaks
break: scala.util.control.Breaks = scala.util.control.Breaks@17eeaaf
scala> break.breakable({
| do{
| print("\t"+a)
| a -= 1
| if(a<=5){
| break.break()
| }
| }while(a>0)
| })
10 9 8 7 6
for循环(重点)
- 迭代遍历数组
scala> var array=Array(1,2,3,4)
array: Array[Int] = Array(1, 2, 3, 4)
scala> for(item <- array){print(item+"\t")}
1 2 3 4
- 通过下标遍历数组
scala> var array=Array(1,2,3,4)
array: Array[Int] = Array(1, 2, 3, 4)
scala> for(i <- 0 until array.length) {
| print(array(i))
| }
- for可使用多个循环因子
scala> for(i <- 1 to 9;j <- 1 to i){
| print(s"${i}*${j} ="+i*j+"\t")
| if(i==j){
| println()
| }
| }
1*1 =1
2*1 =2 2*2 =4
3*1 =3 3*2 =6 3*3 =9
4*1 =4 4*2 =8 4*3 =12 4*4 =16
5*1 =5 5*2 =10 5*3 =15 5*4 =20 5*5 =25
6*1 =6 6*2 =12 6*3 =18 6*4 =24 6*5 =30 6*6 =36
7*1 =7 7*2 =14 7*3 =21 7*4 =28 7*5 =35 7*6 =42 7*7 =49
8*1 =8 8*2 =16 8*3 =24 8*4 =32 8*5 =40 8*6 =48 8*7 =56 8*8 =64
9*1 =9 9*2 =18 9*3 =27 9*4 =36 9*5 =45 9*6 =54 9*7 =63 9*8 =72 9*9 =81
- for和if的使用
scala> for(i<- 0 to 10;if(i%2==0)){
| print(i+"\t")
| }
0 2 4 6 8 10
- for和yield关键字实现元素的提取、创建子集(重点)
scala> var a=Array(1,2,3,4,5)
a: Array[Int] = Array(1, 2, 3, 4, 5)
scala> var res=for(item<-a) yield item*item
res: Array[Int] = Array(1, 4, 9, 16, 25)
match-case(模式匹配)
在Scala语言中剔除了Java中的switch-case
语句,提供了match-case
替代方案,该方案不仅可以按照值匹配,还可以按照类型、以及值的结构(数组匹配、元组匹配、case-class匹配等)
- 值匹配
var a=Array(1,2,3)
var i=a(new Random().nextInt(3))
var res= i match {
case 1 => "one"
case 2 => "two"
case 3 => "three"
case default => null
}
println(res)
- 类型匹配
var a=Array(100,"张三",true,new Date())
var i=a(new Random().nextInt(4))
var res= i match {
case x:Int => s"age:${x}"
case x:String => s"name:${x}"
case x:Boolean => s"sex:${x}"
case _ => "啥都不是"
}
println(res)
注意:
_
表示默认匹配,等价于default
关键字
函数 | 方法
def 方法名(参数1:参数类型,...):返回值类型={
//代码块的最后一行, 默认是方法的返回值,一般可以省略return关键字
}
√标准函数
scala> def sum(x:Int,y:Int):Int={
| x+y
| }
sum: (x: Int, y: Int)Int
一般情况下,如果函数体的最后一行作为函数的返回值,一般可以省略函数的返回值类型,例如:
scala> def sum(x:Int,y:Int)={
| x+y
| }
sum: (x: Int, y: Int)Int
scala> def sum(x:Int,y:Int):Int={
| return x+y
| }
sum: (x: Int, y: Int)Int
如果函数体中有return语句,函数不能省略返回值类型
scala> def sum(x:Int,y:Int)={
| return x+y
| }
<console>:12: error: method sum has return statement; needs result type
return x+y
^
可变长参数
scala> def sum(values:Int*):Int={
| var total=0
| for( i<- values) total += i
| total
| }
sum: (values: Int*)Int
scala> sum(1,2,3,4,5)
res9: Int = 15
scala> def sayHi(msg:String,names:String*):Unit={
| for(name <- names){
| println(msg+"\t"+name)
| }
| }
sayHi: (msg: String, names: String*)Unit
scala> sayHi("Hello","zs")
Hello zs
可变长参数必须放置在最后一位
scala> def sayHi(names:String*,msg:String):Unit={
| for(name <- names){
| println(msg+"\t"+name)
| }
| }
<console>:11: error: *-parameter must come last
def sayHi(names:String*,msg:String):Unit={
^
命名参数
scala> def sayHi(name:String,msg:String):Unit={
| println(s"${msg}\t${name}")
| }
scala> sayHi(msg="hello",name="zhangsan")
hello zhangsan
参数默认值
scala> def sayHi(name:String="未知",msg:String="哈喽"):Unit={
| println(s"${msg}\t${name}")
| }
sayHi: (name: String, msg: String)Unit
scala> sayHi("zhangsan")
哈喽 zhangsan
scala> sayHi(msg="hello")
hello 未知
scala> sayHi("zhangsan","nihao")
nihao zhangsan
内嵌函数(没有要求)
求阶乘案例
def jc2(num:Int):Int={
def compute(x:Int):Int={
if(x>1){
x*compute(x-1)
}else{
1
}
}
compute(num)
}
√柯里化
在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术
def sum1(x:Int,y:Int): Int ={
x+y
}
def sum2(x:Int)(y:Int): Int ={
x+y
}
√匿名函数
将定义的标准函数转化为变量表达的形式,这种表达形式称为匿名函数
scala> def sum(x:Int,y:Int): Int ={
| x+y
| }
sum: (x: Int, y: Int)Int
scala> sum _
res38: (Int, Int) => Int = <function2>
变量名: 变量类型 变量值
val sum:(Int,Int)=>Int = (x,y)=> x+y
--
val i:Int=1
val sum = (x:Int,y:Int)=> x+y
--
val i=1
val sum = ((x,y)=> x+y):((Int,Int)=>Int)
--
val i=1:Byte
def sum:(Int,Int)=>Int = (x,y)=> x+y //lambda表达式
def sum=(x:Int,y:Int)=> x+y
通过以上写法得知:
1)任意一个函数,都可以转变成函数式变量
2)Scala语言中一切皆对象,因此变量也是对象
函数 = = 特殊变量 (函数变量 | 匿名函数)== 对象
scala> def j=1 //定义的是一个常量函数,而不是变量
j: Int
scala> j=10
<console>:12: error: value j_= is not a member of object $iw
j=10
^
面向对象
object
Scala语言中没有静态类和静态方法,但是Scala可以使用object声明静态类,该静态类的所有方法都可以直接被调用
object JdbcUtil {
def sayHello():Unit={
println("你好!")
}
}
var jdbcUtil1=JdbcUtil
var jdbcUtil2=JdbcUtil
println(jdbcUtil1)
println(jdbcUtil2)
println(jdbcUtil1==jdbcUtil2) //true
jdbcUtil1.sayHello()
JdbcUtil.sayHello()
Class-类
- 没有默认构造
class Animal {
def sleep():Unit={
println("sleep...")
}
}
val a1=new Animal
val a2=new Animal
val a3=new Animal()
println(a1==a2) //false
通过class定义的类在创建对象的时候必须使用new关键字
- 指定默认构造器
class User(name:String) {//默认构造
def sayHi():Unit={
println(s"${name}")
}
}
val user = new User("张三")
user.sayHi()
- 扩展默认构造
class User(name:String) {//默认构造
var age:Int= _
def this(name:String,age:Int){ //扩展构造
this(name) //代码第一行必须覆盖默认构造器
this.age=age
}
def sayHi():Unit={
println(s"${name},${age}")
}
}
必须在扩展构造的第一行显示地调用默认构造方法,扩展默认构造使用this关键字
伴生对象
如果class和object在同一个scala源文件中,则称object User 是class User的伴生对象,伴生对象相比较于其它对象有优先操作class User的权限,比如可以使用伴生对象object User快速创建class User的对象:
- apply 工厂方法创建类的对象
class User(name:String){
var age:Int=_
def this(name:String,age:Int){
this(name)
this.age=age
}
}
object User{
def apply(name:String): User = new User(name)
def apply(name: String,age:Int): User = new User(name,age)
}
var u1=new User("zs") //创建
var u2=new User("zs",18)// 创建
var u3=User("zs") //调用apply
var u4=User("zs",18)//调用apply
var u5=User //单例对象
var u6=User //单例对象
var u5=User
var u6=User
- unapply 解码对象的属性
class User(var name:String){
var age:Int=_
def this(name:String,age:Int){
this(name)
this.age=age
}
}
object User{
def unapply(user: User): Option[(String, Int)] = {
if(user==null) {
Some(("",0))
}else{
Some((user.name,user.age))
}
}
def apply(name:String): User = new User(name)
def apply(name: String,age:Int): User = new User(name,age)
}
val u=new User("张三1",18)
val User(name:String,age:Int)=u
println(s"${name},${age}")
抽象类
abstract class Animal(name:String) {
def eat():Unit={
println(s"${name}会吃...")
}
def sleep():Unit
}
Java语法类型,可以有构造方法,但是无法创建对象
特质(Trait)- 接口
trait Flyable {
def fly():Unit
}
trait Speekable {
def speek():Unit={
println("鸟儿会飞")
}
}
类似Java的接口,没有构造方法,可以有默认方法的实现
继承
class Dog(name:String) extends Animal(name:String) with Speekable {
override def sleep(): Unit = {
println(s"${name} 睡觉")
}
override def eat(): Unit = {
println(s"${name} 啃骨头")
}
override def speek(): Unit = {
println(s"${name}汪汪叫")
}
}
动态混入(了解)
class SmallBird(name:String) extends Animal (name:String){
override def sleep(): Unit = {
println(s"${name} 睡觉...")
}
}
var smallBird=new SmallBird("小麻雀") with Flyable{
override def fly(): Unit = {
println("小麻雀会飞了")
}
}
smallBird.sleep()
smallBird.fly()
强制混入(了解)
trait Flyable {
this:Animal=> //强制认定所有Flayable的子类,一定是Animal
def fly():Unit={
println(s"${name} 会飞")
}
}
class Parrot(name:String) extends Animal(name:String) with Flyable {
override def sleep(): Unit = {
println(s"${name}睡觉")
}
}
val parrot = new Parrot("小鹦鹉")
parrot.fly()
self-type(了解)
class Pig(name:String) extends Animal(name:String){
self=> //等价 self:Pig=>
override def sleep(): Unit = {
println(self == this)
println(this.name+" 会睡!")
println(self.name+" 会睡!")
}
}
实际上是混入的一种变体形式
case-class
样例类一般用作数据建模,因此样例类的属性都是只读
(类似元组),样例类在使用的时候无需使用new关键字(==比较的是内容)
case class User(id:Int,name:String,sex:Boolean)
val u1=new User(1,"zs",true)
val u2=new User(1,"zs",true)
val u3=User(1,"zs",true)
println(u1)
println(u2)
注意:样例类与样例类之间不允许出现继承关系
样例类之间可以实现快速拷贝
val u1=new User(1,"zs",true)
val u2=u1.copy(id=2)
println(u1)//User(1,zs,true)
println(u2)//User(2,zs,true)
函数式变量 | 对象
函数接口
在jdk1.8中提出了一个Functional Interface的概念,如果一个接口里面只能有一个抽象方法,这种类型的接口被称为SAM接口(Single Abstract Method Interface),也被称为函数式接口
@FunctionalInterface
public interface IDemoService {
public Integer sum(Integer x,Integer y);
}
@FunctionalInterface
编译检查,确保该接口中只有一个抽象方法
在jdk1.8中提出lambda表达式,用于表示这种函数式接口
IDemoService demoService1=(Integer x, Integer y) -> x+y;
IDemoService demoService2=(x,y)->x+y;
和Java类似,Scala中也提供了类似于Java当中的Functional Interface
,默认情况下,提供了Function022共23个函数式接口,所有的Scala函数或者方法都是Function022的子类,可以使用 _
将任意一个标准函数转换为函数式变量
scala> def sum(x:Int,y:Int): Int ={
| x+y
| }
sum: (x: Int, y: Int)Int
scala> val func=sum _
func: (Int, Int) => Int = <function2>
通常将func
称为sum函数的部分应用函数
,通常使用部分应用函数表示一个函数对象,不难看出func
的字面量是一个<function2>
尝试以下测试:
scala> func.isInstanceOf[Function2[Int,Int,Int]]
res2: Boolean = true
可以看出得到的func
确实是Function2的子类(既然func是Function2的子类,一定可以调用Function2的apply方法实现数据的逻辑计算)
scala> func.apply(1,2)
res3: Int = 3
scala> func(1,2)
res4: Int = 3
在Scala中Function0~Function22存在变体写法(lambda写法):
scala> func.isInstanceOf[(Int,Int)=>Int]
res5: Boolean = true
也就意味着:(Int,Int)=>Int
等价于 Function2[Int,Int,Int]
class SumFunc1 extends Function2[Int,Int,Int] {
override def apply(v1: Int, v2: Int): Int = {
v1+v2
}
}
---
class SumFunc2 extends ((Int,Int) =>Int) {
override def apply(v1: Int, v2: Int): Int = {
v1+v2
}
}
由于标准函数一般无法作为参数单独传递,因此在Java中如果想要传递一个函数,必须将该函数封装成为一个函数式对象
public static Integer process(IDemoService demoService,Integer x,Integer y){
return demoService.sum(x,y);
}
Integer process = process((v1, v2) -> v1 + v2, 1, 2);
System.out.println(process);
可以看出使用Java做Lambda非常麻烦,因为所有Lambda的背后都必须有一个Function Interface
object TestScalaLanguage {
def main(args: Array[String]): Unit = {
println(process1((v1,v2)=>v1+v2,1,3))
println(process2((v1,v2)=>v1*v2,1,3))
}
def process1(fun:Function2[Int,Int,Int],x:Int,y:Int):Int={
fun(x,y)
}
def process2(fun:(Int,Int)=>Int,x:Int,y:Int):Int={
fun(x,y)
}
}
scala> Array(1,2,3).filter(i=>i%2!=0).map(i=>i*i)
res12: Array[Int] = Array(1, 9)
偏函数
偏函数主要适用于处理指定类型的参数数据,通常用于集合处理
val fun=new PartialFunction[Any,Int] {//只处理Int类型元素
//需要处理的类型
override def isDefinedAt(x: Any): Boolean = {
println("isDefinedAt:"+x)
x.isInstanceOf[Int]
}
//应用 函数处理数据
override def apply(v1: Any): Int = {
println("apply:"+v1)
v1.asInstanceOf[Int]+1
}
}
val array = Array(1,2,"a",true,3,4,5)
array.collect(fun)
.foreach(item=>print(item+"\t"))
正常用法:
val a:Any=11
if(fun.isDefinedAt(a)){
fun(a)
}
通常,偏函数还有另外一种写法(表示该偏函数只处理Int类型的数据,如果数据不是Int,系统就抛出异常)
val fun2:PartialFunction[Any,Int] = {case x:Int => x+1}
val array = Array(1,2,"a",true,3,4,5)
array.collect(fun2) //array.collect({case x:Int => x+1})
---
val a= for(i<- array;if(fun2.isDefinedAt(i))) yield fun2(i)
可见性(了解)
Scala中的默认可见性为public,所谓默认,即没有在类或者成员前显示的添加 private 或 protected 可见性关键字,默认都是public(虽然默认是public,但是scala中并没有public关键字)
class Student {
var id:Int=_
var name:String =_
def this(id:Int,name:String){
this()
this.id=id
this.name=name
}
}
var stu=new Student(1,"zs")
println(s"${stu.id}")
private限定
-
private修饰的属性 | 方法
只能被本类以及伴生对象可见,子类不可见
class Student {
private var id:Int=_
private var name:String =_
def getName():String={
name
}
def this(id:Int,name:String){
this()
this.id=id
this.name=name
}
}
object Student{
def unapply(student: Student): Option[(Int,String)] ={
Some(student.id,student.name)
}
}
var stu=new Student(1,"zs")
var Student(id,name)=stu
println(s"${id},${name}")
Scala并不推荐使用私有属性,同时提供公开方法,一般通过伴生对象的unapply解码来获得对象的属性
- private修饰类
该类的所有成员属性对外皆不可见,包括伴生对象,一旦给一个class加上private修饰就直接将该类包括该类的默认构造方法全部隐藏
class Student private() { //修饰的是默认构造
var id:Int=_
var name:String =_
def this(id:Int,name:String){ //扩展构造
this()
this.id=id
this.name=name
}
def sayHello():Unit={
println("hello")
}
}
object Student{
def apply(id:Int,name:String): Student = new Student(id,name)
def unapply(student: Student): Option[(Int,String)] ={
Some(student.id,student.name)
}
}
private class Student { //修饰的是类,一般用在内部类中
//...
}
采用这种写法,该Student类对外不可见,同修饰内部类
class Master {//师傅
private class Slave{ //徒弟
}
def teach():Unit={
val slave = new Slave()
println(slave)
}
}
protected 限定
-
修饰属性 | 方法
可以被子类以及子类的伴生对象可见
class Student {
protected var id:Int=_
protected var name:String =_
protected def say():Unit={
println(s"${name},${id}")
}
}
class SmallStudent extends Student {
def this(id:Int,name:String){
this()
this.id=id
this.name=name
}
}
- 修饰类
该类只能被本包下的子类所继承,在继承的时候 并不继承父类的可见性
package com.dingl.demo09
protected class Student { //只可以被本包子类继承,其它包下不可见
protected var id:Int=_
protected var name:String =_
protected def say():Unit={
println(s"${name},${id}")
}
}
class SmallStudent extends Student {
def this(id:Int,name:String){
this()
this.id=id
this.name=name
}
override def say(): Unit = { //默认情况下,子类在继承父类的方法时,会继承方法的可见性,覆盖父类方法的可见性
println(s"${id},${name}")
}
}
如果子类不覆盖父类的方法,在继承父类方法的时候,也会继承父类方法的可见性
this限定
当被this限定修饰,该方法或者属性只能在本类内部可见,去除伴生对象
class Animal {
private|protected[this] var name:String=_ //去除伴生对象的可见性
def this(name:String){
this()
this.name=name
}
def say():Unit={
println(s"${name}")
}
}
包限定
package com.dingl.demo11
class Animal {
private[demo11] def say():Unit={ //所有在demo11包下的Animal类及其子类都可看到say方法,此时 private的限定失去了意义
println("animal say")
}
}
常见关键字
final 关键字
修饰类别 | Java | Scala |
---|---|---|
类 | 最终类,没有子类 | 最终类,没有子类 |
方法 | 不能被覆盖 | 不能被覆盖 |
属性 | 常量,不允许修改 | 属性不允许被子类遮盖(val属性) |
class Animal {
final val name:String="大黄"
def say():Unit={
println(s"${name}")
}
}
---
class Dog extends Animal {
override val name:String="小黑" //错误
}
√lazy关键字
被lazy修饰的val常量,只有被真正使用时才会被初始化
lazy val num=sum(1,2)
println(s"${num}")
def sum(x:Int,y:Int):Int={
println("===========")
x+y
}
sealed关键字
修饰一个类,被sealed修饰的类及其子类必须放置在同一个源文件中
sealed class Message(content:String)
case class SmsMessage(content:String,phone:String) extends Message(content:String)
case class EmailMessage(content:String,email:String) extends Message(content:String)
val messages = Array[Message](SmsMessage("hello","110"),EmailMessage("你好","11@qq.com"))
messages.foreach(message=>{
message match {
case m:SmsMessage =>{
println("收到短信:"+m.phone)
}
case m:EmailMessage=>{
println("收到邮件:"+m.email)
}
}
})
隐式转换/注入/增强 - 重点
implicit 变量- 隐式值
一般定义隐式值的目的是为了隐式注入,要求同一个程序的上下文中只能有一种类型的隐式值
scala> implicit var a:Int=100
a: Int = 100
scala> var m=implicitly[Int] //按照类型注入
m: Int = 100
implicit 修饰参数- 隐式
object MyImplicits {
implicit var i= 100 //确保隐式值类型 只能有一个
implicit val f=(x:Int,y:Int)=>x+y
}
def process(x:Int,y:Int)(implicit fun:(Int,Int)=>Int):Int={
fun(x,y)
}
import com.dingl.demo13.MyImplicits._
var result2=process(1,2)
println(s"${result2}")
implicit 修饰方法- 隐式转换 不兼容变成兼容
def tellTime(date:Date):Unit={
println(s"${date.toLocaleString}")
}
import com.dingl.demo13.MyImplicits._
tellTime("2019-9-20 16:34:50") // 等价于 tellTime(strDate("2019-9-20 16:34:50"))
tellTime(new Date())
object MyImplicits {
implicit def strDate(str:String):Date={//参数表示要转换的类型,返回值表示需要的目标类型
val sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
sdf.parse(str)
}
}
implicit 修饰类- 隐式增强 不可能变可能
class Pig {
def sleep():Unit={
println("sleep")
}
def eat():Unit={
println("eat")
}
}
object MyImplicits {
implicit class PigImplicits(pig:Pig){
def fly():Unit={
println("pig 会飞")
}
}
}
import com.dingl.demo13.MyImplicits._
val pig = new Pig()
pig.eat()
pig.sleep()
pig.fly() //增强方法
Scala泛型(看懂)
<:上界限定
def keep[T<:Dog](t:T):Unit={
println(t)
}
要求T必须是Dog或者Dog的子类
>:下界限定
def keep[T >: Dog](t:T):Unit={
println(t)
}
T必须是Dog或者Dog的父类
<%视图限定
class SmallDog(name:String) extends Dog {
def speek():Unit={
println(s"${name} 汪汪叫~")
}
}
def playWithSmallDog[T<% SmallDog](dog:T):Unit={
dog.speek()
}
implicit def str2SmallDog(str:String):SmallDog={
new SmallDog(str)
}
playWithSmallDog("小狗")
强制将T看做成是SmallDog类型,要求用户在调用的时候必须制定一个隐式转换。将T转换为SmallDog
A:T上下文限定
def compare[T:MyOrder](t1:T, t2:T):Unit={
val myOrder=implicitly[MyOrder[T]]
myOrder.compareT(t1,t2)
}
表示上下文中一定会有一个隐式值MyOder[T]隐士值。
+A协变
class DogKeeper[+T] {}
var dogKeeper1=new DogKeeper[SmallDog]
var dogKeeper2=new DogKeeper[Dog]
var dogKeeper3=new DogKeeper[Animal]
dogKeeper3=dogKeeper2
dogKeeper2=dogKeeper1
将DogKeeper[Dog]赋值给DogKeeper[Animal]
-A逆变
class DogKeeper[-T] {}
var dogKeeper1=new DogKeeper[SmallDog]
var dogKeeper2=new DogKeeper[Dog]
var dogKeeper3=new DogKeeper[Animal]
dogKeeper2=dogKeeper3
dogKeeper1=dogKeeper2
将DogKeeper[Animal]赋值给DogKeeper[Dog]
A不变
class DogKeeper[T] {}
var dogKeeper1=new DogKeeper[SmallDog]
var dogKeeper2=new DogKeeper[Dog]
var dogKeeper3=new DogKeeper[Animal]
只能将DogKeeper[Dog]赋值给DogKeeper[Dog]
集合/数组(重点)
常见集合数组(了解)
scala> var array=Array(1,2,3,4,5)
array: Array[Int] = Array(1, 2, 3, 4, 5)
scala> array(0)= -1
scala> array(0)
res2: Int = -1
scala> array.length
res4: Int = 5
scala> array.size
res5: Int = 5
scala> var range =new Range(0,10,2)
range: scala.collection.immutable.Range = Range(0, 2, 4, 6, 8)
scala> var range= 0 to 10 by 2
range: scala.collection.immutable.Range = Range(0, 2, 4, 6, 8, 10)
scala> var range= 0 until 10 by 2
range: scala.collection.immutable.Range = Range(0, 2, 4, 6, 8)
scala> var vec=Vector(0,10,1)
vec: scala.collection.immutable.Vector[Int] = Vector(0, 10, 1)
scala> vec(1)
res12: Int = 10
scala> vec(2)
res13: Int = 1
scala> for(i<- 0 to 10 by 2) yield i
res15: scala.collection.immutable.IndexedSeq[Int] = Vector(0, 2, 4, 6, 8, 10)
- Iterator
scala> var it=Iterator(1,2,3)
it: Iterator[Int] = non-empty iterator
scala> for(i<- it) println(i)
1
2
3
scala> for(i<- it) println(i)
scala> var it=Iterator(1,2,3)
it: Iterator[Int] = non-empty iterator
scala> it.size
res21: Int = 3
scala> it.size
res22: Int = 0
- List (只读)
scala> val list = List(1,2,3,4,5)
list: List[Int] = List(1, 2, 3, 4, 5)
scala> list.size
res0: Int = 5
scala>
scala> list.+:(-1) //返回新的List,原始数据不变
res1: List[Int] = List(-1, 1, 2, 3, 4, 5)
scala> list.::(-1) //追加元素
res2: List[Int] = List(-1, 1, 2, 3, 4, 5)
scala>
scala> list.++(List(0,0)) //向后追加,元素
res3: List[Int] = List(1, 2, 3, 4, 5, 0, 0)
scala> list.++:(List(0,0))//向前追加,元素
res4: List[Int] = List(0, 0, 1, 2, 3, 4, 5)
scala> list.:::(List(1,3)) //向前追加 元素
res5: List[Int] = List(1, 3, 1, 2, 3, 4, 5)
scala>
ListBuffer(支持修改)
scala> var listBuffer=new ListBuffer[Int]
listBuffer: scala.collection.mutable.ListBuffer[Int] = ListBuffer()
scala> listBuffer.+=(1)//添加元素
res22: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1)
scala> listBuffer.update(0,-1)//修改0位置为-1
scala> listBuffer.-=(-1)//删除元素
res24: scala.collection.mutable.ListBuffer[Int] = ListBuffer()
scala> listBuffer.++=(Array(1,2,3,4))
res25: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 3, 4)
scala> listBuffer.remove(1)
res26: Int = 2
scala> listBuffer
res27: scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 3, 4)
Set(不可变)
scala> var set=Set(1,2,3,4,5,1)
set: scala.collection.immutable.Set[Int] = Set(5, 1, 2, 3, 4)
scala> set.+(-1)
res35: scala.collection.immutable.Set[Int] = Set(5, 1, 2, 3, -1, 4)
scala> set.size
res36: Int = 5
scala> set.+=(10)
scala> set
res38: scala.collection.immutable.Set[Int] = Set(5, 10, 1, 2, 3, 4)
Set(可变集合)
scala> var set=scala.collection.mutable.Set(1,2,3,4,5,1)
set: scala.collection.mutable.Set[Int] = Set(1, 5, 2, 3, 4)
scala> set.+(-1)
res51: scala.collection.mutable.Set[Int] = Set(1, 5, 2, -1, 3, 4)
scala> set.size
res52: Int = 5
scala> set.+=(10) //添加元素,修改自己
res53: scala.collection.mutable.Set[Int] = Set(1, 5, 2, 3, 10, 4)
scala> println(set.add(1)) //表示添加是否成功
false
scala> set.remove(1)
res55: Boolean = true
- HashMap(不可变)
import scala.collection.mutable.HashMap
var hm=HashMap[String,String](("001","zhangsan"),("002","李四"))
//val hm1=Map[String,String](("001","张三"),("002","lisi"))
hm.-("001") //删除key,不修改原始值
hm.+(("003","王五")) //添加值,不修改原始值
//获取Map的值
val value = hm.get("004").getOrElse("没有值")
//迭代key
for(k<-hm.keys){
println(hm.get(k).getOrElse(""))
}
val size = hm.size
//迭代值
for(i<-hm.values) println(i)
- HashMap(可变)
var hm=HashMap[String,String](("001","zhangsan"),("002","李四"))
//val hm1=Map[String,String](("001","张三"),("002","lisi"))
hm.-("001") //删除key,不修改原始值
hm.+(("003","王五")) //添加值,不修改原始值
hm.put("004","赵六")
hm.remove("004")
//获取Map的值
val value = hm.get("004").getOrElse("没有值")
//迭代key
for(k<-hm.keys){
println(hm.get(k).getOrElse(""))
}
val size = hm.size
//迭代值
for(i<-hm.values) println(i)
集合计算(必须掌握)
为何要学习Scala集合计算?
算子学习
参考:https://blog.csdn.net/weixin_38231448/article/details/89354185
√ filter算子
用于对集合数据List[T]做过滤,需要传递一个 fn: T => Boolean
scala> List(1,2,3,4,5).filter(item => item %2 ==0) //List(1,2,3,4,5).filter(_ % 2 ==0)
res11: List[Int] = List(2, 4)
scala> val list=List("hello ni hao","hello world","hello scala")
list: List[String] = List(hello ni hao, hello world, hello scala)
scala> list.filter(item=> item.contains("scala"))// list.filter(_.contains("scala"))
res13: List[String] = List(hello scala)
还有一个filterNot算子和filter相反,将满足条件的元素去除。
scala> List(1,2,3,4,5).filterNot(_ % 2 ==0)
res19: List[Int] = List(1, 3, 5)
flatten
用作数组数据降维,例如List[Array[T]] 通过flatten转换可以得到List[T]
scala> val list=List(Array(1,2,3),Array(4,5))
list: List[Array[Int]] = List(Array(1, 2, 3), Array(4, 5))
scala> list.flatten
res21: List[Int] = List(1, 2, 3, 4, 5)
√ map算子
用作数组元素的转换List[T]集合需要提供 fun:T=>U将List[T]转换为List[U]
scala> List("a","b","c").map(item=> (item,1))
res22: List[(String, Int)] = List((a,1), (b,1), (c,1))
scala> val words=List("this is a demo","hello scala").map(line=>line.split(" ")).flatten
words: List[String] = List(this, is, a, demo, hello, scala)
scala> words.map(word=>(word,1))
res27: List[(String, Int)] = List((this,1), (is,1), (a,1), (demo,1), (hello,1),
(scala,1))
√flatMap等价 1.map 2. flatten
专门用于将集合中的元素先进行map转换 List[T] ,尝试将元素T转换为List[U],一般需要提供fun: T =>List[U],这样原始的List[T]就变成List[List[U]],然后该算子会执行flatten将List[List[U]]展开成为List[U]
scala> val lines=List("this is a demo","hello scala")
lines: List[String] = List(this is a demo, hello scala)
scala> val words=lines.flatMap(line=>line.split(" "))
words: List[String] = List(this, is, a, demo, hello, scala)
scala> val linesplits=lines.map(line=> line.split(" "))
linesplits: List[Array[String]] = List(Array(this, is, a, demo), Array(hello, sc
ala))
scala> linesplits.flatten
res36: List[String] = List(this, is, a, demo, hello, scala)
√sort算子
sorted
scala> val nums = List(2,3,5,1,4,6)
nums: List[Int] = List(2, 3, 5, 1, 4, 6)
scala> nums.sorted
res37: List[Int] = List(1, 2, 3, 4, 5, 6)
scala> nums.sorted(new Ordering[Int]{
| override def compare(x: Int, y: Int): Int = {
| (x-y) * -1
| }
| })
res39: List[Int] = List(6, 5, 4, 3, 2, 1)
sortBy
scala> var users=List((1,"zs",18),(2,"lisi",20))
users: List[(Int, String, Int)] = List((1,zs,18), (2,lisi,20))
scala> users.sortBy(user=>user._3)
res41: List[(Int, String, Int)] = List((1,zs,18), (2,lisi,20))
scala> users.sortBy(user=>user._3)(new Ordering[Int]{
| override def compare(x: Int, y: Int): Int = {
| (x-y) * -1
| }
| })
res42: List[(Int, String, Int)] = List((2,lisi,20), (1,zs,18))
sortWith
scala> var users=List((1,"zs",18),(2,"lisi",20))
users: List[(Int, String, Int)] = List((1,zs,18), (2,lisi,20))
scala> users.sortWith((u1,u2)=> u1._3 > u2._3)
res45: List[(Int, String, Int)] = List((2,lisi,20), (1,zs,18))
scala> users.sortWith((u1,u2)=> u1._3 < u2._3)
res46: List[(Int, String, Int)] = List((1,zs,18), (2,lisi,20))
√groupBy
scala> val list=List("a","b","a","b","c")
list: List[String] = List(a, b, a, b, c)
scala> list.groupBy(c=>c)
res47: scala.collection.immutable.Map[String,List[String]] = Map(b -> List(b, b)
, a -> List(a, a), c -> List(c))
有如下数组:List(“this is a demo”,"good good study ",“day day up”,"come on baby "),请输出格式数据
List((this,1),(good,2),…)
scala> val lines=List("this is a demo","good good study ","day day up","come on
baby")
scala> val wordpair=lines.flatMap(line=>line.split(" ")).map(word=>(word,1))
wordpair: List[(String, Int)] = List((this,1), (is,1), (a,1), (demo,1), (good,1)
, (good,1), (study,1), (day,1), (day,1), (up,1), (come,1), (on,1), (baby,1))
scala> val group=wordpair.groupBy(word=>word._1).toList
group: List[(String, List[(String, Int)])] = List((this,List((this,1))), (demo,L
ist((demo,1))), (is,List((is,1))), (good,List((good,1), (good,1))), (up,List((up
,1))), (a,List((a,1))), (come,List((come,1))), (on,List((on,1))), (baby,List((ba
by,1))), (day,List((day,1), (day,1))), (study,List((study,1))))
scala> group.map(t=>(t._1,t._2.size))
res53: List[(String, Int)] = List((this,1), (demo,1), (is,1), (good,2), (up,1),
(a,1), (come,1), (on,1), (baby,1), (day,2), (study,1))
sum
只能针对数值类型的数组做计算
scala> List(1,2,4,5).sum
res57: Int = 12
count
计算数组中数据的个数
scala> List("a","c","c").count(item=>true)
res63: Int = 3
max|min|maxBy|minBy
scala> List(1,3,4,5,6).max
res65: Int = 6
scala> List(1,3,4,5,6).min
res66: Int = 1
scala> List(("a",1),("a",2),("c",3)).maxBy(t=>t._2)
res68: (String, Int) = (c,3)
scala> List(("a",1),("a",2),("c",3)).maxBy(t=>t._1)
res69: (String, Int) = (c,3)
scala> List(("a",1),("a",2),("c",3)).minBy(t=>t._2)
res70: (String, Int) = (a,1)
√reduce
scala> List(1,2,3,4).reduce((v1,v2)=> v1+v2)//scala> List(1,2,3,4).reduce(_+_)
res71: Int = 10
√aggregate
scala> val ints = List(1,2,3,4,5,6)
ints: List[Int] = List(1, 2, 3, 4, 5, 6)
scala> val tuple = ints.aggregate((0.0,0))((z,v)=>(z._1+v,z._2 +1),(b1,b2)=>
(b1._1+b2._1,b1._2+b2._2))
tuple: (Double, Int) = (21.0,6)
scala> var avg=tuple._1/tuple._2
avg: Double = 3.5
√fold
scala> val ints = List[Int]()
ints: List[Int] = List()
scala> ints.fold(0)((v1,v2)=>v1+v2)
res2: Int = 0
scala> val ints = List[Int](1,2,3,4)
ints: List[Int] = List(1, 2, 3, 4)
scala> ints.fold(0)((v1,v2)=>v1+v2)
res4: Int = 10
字符统计
scala> var arrs=Array("this is a demo","good good study","day day up")
arrs: Array[String] = Array(this is a demo, good good study, day day up)
scala> arrs.flatMap(line=>line.split(" "))
.groupBy(word=>word).toList
.map(t=>(t._1,t._2.size))
.sortBy(t=>t._2)
res18: List[(String, Int)] = List((this,1), (demo,1), (is,1), (up,1), (a,1), (st
udy,1), (good,2), (day,2))
scala> arrs.flatMap(line=>line.split(" "))
.map((_,1))
.groupBy(t=>t._1)
.toList.map(t=>(t._1,t._2.map(_._2).sum))
.sortBy(t=>t._2)
res19: List[(String, Int)] = List((this,1), (demo,1), (is,1), (up,1), (a,1), (st
udy,1), (good,2), (day,2))
y
scala> List(1,3,4,5,6).max
res65: Int = 6
scala> List(1,3,4,5,6).min
res66: Int = 1
scala> List(("a",1),("a",2),("c",3)).maxBy(t=>t._2)
res68: (String, Int) = (c,3)
scala> List(("a",1),("a",2),("c",3)).maxBy(t=>t._1)
res69: (String, Int) = (c,3)
scala> List(("a",1),("a",2),("c",3)).minBy(t=>t._2)
res70: (String, Int) = (a,1)
√reduce
scala> List(1,2,3,4).reduce((v1,v2)=> v1+v2)//scala> List(1,2,3,4).reduce(_+_)
res71: Int = 10
√aggregate
scala> val ints = List(1,2,3,4,5,6)
ints: List[Int] = List(1, 2, 3, 4, 5, 6)
scala> val tuple = ints.aggregate((0.0,0))((z,v)=>(z._1+v,z._2 +1),(b1,b2)=>
(b1._1+b2._1,b1._2+b2._2))
tuple: (Double, Int) = (21.0,6)
scala> var avg=tuple._1/tuple._2
avg: Double = 3.5
√fold
scala> val ints = List[Int]()
ints: List[Int] = List()
scala> ints.fold(0)((v1,v2)=>v1+v2)
res2: Int = 0
scala> val ints = List[Int](1,2,3,4)
ints: List[Int] = List(1, 2, 3, 4)
scala> ints.fold(0)((v1,v2)=>v1+v2)
res4: Int = 10
字符统计
scala> var arrs=Array("this is a demo","good good study","day day up")
arrs: Array[String] = Array(this is a demo, good good study, day day up)
scala> arrs.flatMap(line=>line.split(" "))
.groupBy(word=>word).toList
.map(t=>(t._1,t._2.size))
.sortBy(t=>t._2)
res18: List[(String, Int)] = List((this,1), (demo,1), (is,1), (up,1), (a,1), (st
udy,1), (good,2), (day,2))
scala> arrs.flatMap(line=>line.split(" "))
.map((_,1))
.groupBy(t=>t._1)
.toList.map(t=>(t._1,t._2.map(_._2).sum))
.sortBy(t=>t._2)
res19: List[(String, Int)] = List((this,1), (demo,1), (is,1), (up,1), (a,1), (st
udy,1), (good,2), (day,2))