Scala编程指南-第二版

Scala Language

语言介绍

Scala是一门多范式的编程语言,同时支持面向对象面向函数编程风格。它以一种优雅的方式解决现实问题。虽然它是强静态类型的编程语言,但是它强大的类型推断能力,使其看起来就像是一个动态编程语言一样。Scala语言最终会被翻译成java字节码文件,可以无缝的和JVM集成,并且可以使用Scala调用java的代码库。除了Scala编程语言自身的特性以外,目前比较流行的Spark计算框架也是使用Scala语言编写。Spark 和 Scala 能够紧密集成,例如 使用Scala语言操作大数据集合的时候,用户可以像是在操作本地数据集那样简单操作Spark上的分布式数据集-RDD(这个概念是Spark 批处理的核心术语),继而简化大数据集的处理难度,简化开发步骤。

编程指南: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 plugin from disk选项,选择 scala-intellij-bin-2018.2.11.zip安装成功后,重启IDEA。

常见变量

Scala是纯粹的面向对象编程语言(Java做对比 基本类型和对象类型),在Scala没有基本类型。所有类型都属于对象类型。
在这里插入图片描述

变量声明

Java变量声明

变量类型 变量名= 变量值;
int a=1;

Scala变量声明

(var|val)  变量名[:变量类型] = 变量值[:变量类型]
变量 |常亮
scala> var i:Int = 1:Int
i: Int = 1

scala> var i = 1
i: Int = 1

scala> var i:Byte = 1
i: Byte = 1

scala> var i = 1:Byte
i: Byte = 1

scala> i=127
i: Byte = 127

scala> i=128
<console>:12: error: type mismatch;
 found   : Int(128)
 required: Byte
       i=128
         ^

正常情况下用户可以省略类型说明,因为Scala可以根据字面量自动推断出变量的类型,所以在定义一个变量的时候用户只需指明该变量时变量还是常量即可。

scala> var j="hello word"
j: String = hello word

scala> var sex=true
sex: Boolean = true

scala> sex="false"
<console>:12: error: type mismatch;
 found   : String("false")
 required: Boolean
       sex="false"

注意var修饰变量值可以改变,val修饰常量 值不可改变等价于Java中final修饰的变量。

scala> var i=10
i: Int = 10

scala> i=11
i: Int = 11

scala> val j=10
j: Int = 10

scala> j=11
<console>:12: error: reassignment to val
       j=11
        ^

数值转换

  • 类型兼容且 由小到大,由整数到小数,可以自动类型提升
scala> var i:Byte = 1
i: Byte = 1

scala> var j:Byte = 1
j: Byte = 1

scala> var k = i+j
k: Int = 2

scala> var m = k + 0.5
m: Double = 2.5

scala> var f:Float=m
<console>:12: error: type mismatch;
 found   : Double
 required: Float
       var f:Float=m
                   ^
  • 类型兼容,且由大到小 asInstanceOf
scala> var d:Double = 100.0
d: Double = 100.0

scala> var f:Float = d
<console>:12: error: type mismatch;
 found   : Double
 required: Float
       var f:Float = d
                     ^
scala> var f:Float = d.asInstanceOf[Float]
f: Float = 100.0

在Scala中[]表示的是泛型.

  • 将字符串类型转为数值类型
scala> var sex=true
sex: Boolean = true

scala> sex="false"
<console>:12: error: type mismatch;
 found   : String("false")
 required: Boolean
       sex="false"
           ^

scala> sex="false".toBoolean
sex: Boolean = false

scala> var i=1
i: Int = 1

scala> i ="123".toInt
i: Int = 123

注意在类型兼容前提下 大-> 小 使用 asInstanceOf[类型],如果是字符串类型转为值类型可以使用toInt/Double/Float/Char/Long/Byte/Short等。

Unit类型

在Scala中通常会使用Unit关键字表示一种特殊的返回值类型,等价于Java中Void关键字。但是在Scala中Unit是有字面量的。

scala> var u:Unit=()
u: Unit = ()

Array类型(数组类型)

scala> var a:Array[Int] = new Array[Int](5) # 直接指定数组大小,并且赋值默认值
a: Array[Int] = Array(0, 0, 0, 0, 0)

scala> var a:Array[Int]=Array(1,2,3,4,5) # 一种快速创建复杂对象方式,后续课程会讲解(伴生对象)
a: Array[Int] = Array(1, 2, 3, 4, 5)

scala> a.length # 获取数组长度
res6: Int = 5

scala> a.size  # 获取数组长度
res7: Int = 5

scala> a(0) = -1 # 修改数组内容

scala> a
res9: Array[Int] = Array(-1, 2, 3, 4, 5)

scala> a(5) = -1
java.lang.ArrayIndexOutOfBoundsException: 5
  ... 32 elided

元组(Tuple)

由若干个元素构成的复合变量

scala> var user = ("zhangsan",18,true,15000)
user: (String, Int, Boolean, Int) = (zhangsan,18,true,15000)

scala> var a:(String,Int)=("hello",1)
a: (String, Int) = (hello,1)

scala> user._1  #访问元组中第几个元素,下标从1开始,最多到
res12: String = zhangsan

scala> user._1 = "wangwu" #元组中的元素都是val,不允许修改
<console>:12: error: reassignment to val
       user._1 = "wangwu"
               ^

元组中元素都是只读的,一般有值类型和String类型构成。在Scala一个Tuple最多能够有22个成员。元组中的元素都是val类型不允许修改(只读),但是元组变量可以修改

scala> var user = ("zhangsan",18,true,15000)
user: (String, Int, Boolean, Int) = (zhangsan,18,true,15000)

scala> user = ("wangwu",20,false,20000)
user: (String, Int, Boolean, Int) = (wangwu,20,false,20000)

分支循环

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 语句,在执行到该语句时,就会中断循环并执行循环体之后的代码块。

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 函数名(参数:参数类型,...):[返回值类型] = {
    //方法实现
}

标准函数

def sum(x:Int,y:Int):Int = {
    return x+y
}
或者
def sum02(x:Int,y:Int)= {
    x+y
}

可以省略return关键字以及返回值类型,系统一般可以自动推断。

可变长参数

def sum03(values:Int*)= {
    var total=0 
    for(i <-values){
        total+=i
    }
    total
}

注意:可变长参数必须放置在最后

命名参数

def sayHello(msg:String,name:String):Unit={
    println(s"${msg}~ ${name}")
}
sayHello("hello","张三")
sayHello(name="张三",msg="hello")

参数默认值

def sayHello02(msg:String="哈喽",name:String):Unit={
    println(s"${msg}~ ${name}")
}
def sayHello03(name:String,msg:String="哈喽"):Unit={
    println(s"${msg}~ ${name}")
}
sayHello02(name="张三")
sayHello03("李四")

内嵌函数

def factorial(x:Int):Int={
    def mulit(i:Int):Int={
        if(i>1){
            i*mulit(i-1)
        }else{
            1
        }
    }
    mulit(x)
}
println(factorial(5))

柯里化(Currying)

在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数的函数,并且返回接受余下的参数且返回结果的新函数的技术。

def sum02(x:Int,y:Int)= {
    x+y
}
====
def sum04(x:Int)(y:Int):Int= {
    x+y
}
scala> def sum04(x:Int)(y:Int):Int= {
     |     x+y
     |   }
sum04: (x: Int)(y: Int)Int

scala> sum04(1)(2)
res41: Int = 3

scala> sum04(1)(_)
res45: Int => Int = <function1>

scala> var s=sum04(1)(_)  # 部分应用函数
s: Int => Int = <function1>

scala> s(2)
res47: Int = 3

scala> var s2=sum04 _ #部分应用函数
s2: Int => (Int => Int) = <function1>

scala> s2(1)
res52: Int => Int = <function1>

scala> s2(1)(2)
res53: Int = 3

匿名函数|方法

只有参数列表和返回值类型,没有方法名,通常使用匿名函数形式去声明一个函数式变量。

scala> var sum=(x:Int,y:Int)=> {x+y}
sum: (Int, Int) => Int = <function2>

scala>   var multi=(x:Int,y:Int)=> {x*y}
multi: (Int, Int) => Int = <function2>

scala> println(sum(1,2)+" | "+multi(1,2))
3 | 2

def关键字可以定义一个函数式变量。也可以声明一个标准方法。

def sum(x:Int,y:Int):Int = {
    return x+y
}
def sum=(x:Int,y:Int)=>x+y
var sum=(x:Int,y:Int)=>x+y
var sum:(Int,Int)=> Int = (x,y)=> x+y
var sum = ((x,y)=> x+y):(Int,Int)=> Int

通过上述变换可以托导出 函数可以转换为变量,因此可以使用var或者val修饰函数式变量,又因为函数式变量时函数,所以可以使用def修饰。

def method(op:(Int,Int)=>Int)(x:Int,y:Int)={
    op(x,y)
}
val result01 = method((x,y)=>x+y)(1,2)
val result02 = method((x,y)=>x*y)(1,2)
println(result01)
println(result02)

Class & object

由于Scala没有静态方法和静态类,通过object去定义静态方法或者静态对象。当object和Class放在一个文件中时候称该object为当前Class的伴生对象。

单例类

单例类使用object修饰,所有声明在object中的方法都是静态方法,类似于Java中声明的工具类的作用。

object SingleObject {
    def sum(x:Int,y:Int):Int={
      x+y
    }
}
var so1=SingleObject
var so2=SingleObject

println(so1==so2) //true
SingleObject.sum(1,2)

class User(var id:Int,var name:String){     //默认构造器
  var salary:Double = _

  def this(id:Int,name:String,salary:Double){//扩展构造器
    this(id,name)
    this.salary=salary
  }

  override def toString = s"User($salary, $id, $name)"
}

注意扩展构造器的第一行必须调用默认构造器_表示默认值

var u=new User(2,"zhangsan")
u.name="lisi"
println(u.id)
println(u.name)

var u2=new User(3,"王五",15000)
println(u2)

伴生对象

如果类和object在一个scala文件中,则称为object User 是class User的伴生对象,使用伴生对象可以方便的创建对象,只需要覆盖对应的apply方法,如下:

class User(var id:Int, var name:String){//默认构造器
  var salary:Double = _

  def this(id:Int,name:String,salary:Double){//扩展构造器
    this(id,name)
    this.salary=salary
  }
    
  override def toString = s"User($salary, $id, $name)"
}
object User{//伴生对象
  def apply(id: Int, name: String): User = new User(id, name)
  def apply(id: Int, name: String,salary:Double): User = {
    val user = new User(id,name)
    user.salary=salary
    user
  }

  def unapply(arg: User): Option[(Int, String,Double)] ={
    Some(arg.id,arg.name,arg.salary)
  }
}

这里可以理解apply是一个工厂方法,该方法的作用是生产User实例对象。使用unapply方法能够将对象中的一些属性反解出来.这里需要注意Option的返回值类型只有None或者是Some,由于Some和None都是caseclass,所以在创建的时候可以省略new关键字,针对于caseclass后续章节会介绍。

抽象类

和Java中抽象类类似,不可以创建对象,但是可以有构造。使用abstract修饰。

abstract class Animal(name:String) {
  def sleep(): Unit ={
    println(s"${name} 会睡觉!")
  }
  def eat():Unit
}

Trait-特质

类似于Java中的接口,不可以创建对象,也不可以有构造。因为Scala运行在Java8之上,所以具备在特质声明方法的默认实现。

trait Speakable {
  def speak():Unit
}
trait Flyable {
  def fly():Unit={
    println("会飞~")
  }
}

继承&实现

class Dog(name:String) extends Animal(name:String) {

  override def eat(): Unit = {
    println(s"${name} 啃骨头")
  }
}
class SmallParrot(name:String )extends Animal(name:String) with Flyable {
  def eat(): Unit = {
    println(s"${name} 嗑瓜子!")
  }

  override def fly(): Unit = {
    println(s"${name} 没长大,还不会飞")
  }
}
var d:Animal=new Dog("小黑")
d.sleep()
d.eat()

var a=new SmallParrot("小鹦鹉")
a.sleep()
a.eat()
a.fly()

如果抽象类或者是特质中有方法实现,在覆盖的时候必须添加override,如果实现多个特质

class A extends B with C with D{

}

B:有可能是抽象类|类,也有可能Trait,C、D一定是特质,Scala依然保持java单继承多实现。

特质动态织入

class Pig(name:String) extends  Animal(name:String) with Speakable {
  override def speak(): Unit = {
    println("哼哼~")
  }

  override def eat(): Unit = {
    println(s"${name} 给啥吃啥~")
  }
}
var pig=new Pig("猪坚强") with Flyable{
    override def fly(): Unit = {
        println(s"${name} 会飞了~")
    }
}
pig.speak()
pig.eat()
pig.sleep()
pig.fly()

self

如果在Scala的类中this关键字可以起别名。要求必须写在类代码块的第一行

class User(id:Int,name:String) {
  self =>//必须第一行
  var salary:Double=_
  def this(id:Int,name:String,salary:Double){
    this(id,name)
    self.salary=salary // this.salary=salary
  }
}

Trait强制混合

abstract class Animal(var name:String) {
}
trait Flyable {
  this:Animal => //强制所有混入Flyable的类,必须是Animal子类
  def  fly():Unit={
    println(s"${name} 会飞!")
  }
}
class Parrot(name:String) extends Animal(name:String) with Flyable {}

case-class

case class就像常规类,case class适用于对不可变数据进行建模,case-class 比较是内容,创建case-class实例可以省略new关键字

var u1= User(1,"zs",true)
var u2= User(1,"zs",true)
var u3= new User(1,"zs",true)
println(u1==u2)
println(u3==u2)

case class的所有属性都是只读,不允许修改,通常用于只读数据的建模。可以简单的使用copy来实现两个对象间的值得传递.

var u3= new User(1,"zs",true)
var u4=u3.copy(id=10)

case class之间不允许出现继承关系。

可见性

private

  • 修饰类:只能被同包子类所继承,而且子类也必须是片private修饰
package com.baizhi.demo09

private class Student {
  private var id:Int=_
  var name:String=_

  private def sayHello():Unit={
    println(s"Hello~ ${name}")
  }
}
---
package com.baizhi.demo09

private class SmallStudent extends Student {

}

  • 修饰属性|方法:只能被本类以及伴生对象中可见。但是伴生对象无法使用apply创建Student对象
package com.baizhi.demo09

private class Student {
  private var id:Int=_
  var name:String=_

  private def sayHello():Unit={
    println(s"Hello~ ${name}")
  }
}
object Student{
  
  def main(args: Array[String]): Unit = {
    var s=new Student
	s.sayHello()
  }
}

protected

  • 修饰类:可以被同包子类所继承。对子类修饰符没有要求(子类继承父类的可见性),伴生对象可以创建本类对象
protected class Student01 {
  protected var id:Int=10
}
object Student01{
  def apply: Student01 = new Student01()
  
}

class SmallStudent01 extends Student01 {
    def say():Unit={
      println(s"${id}")
    }
}

必须在同一个包下

  • 属性和方法:属性和方法只可以在同包下子类以及子类的伴生对象中可见

this限定

可以和private或者protected联合使用,可以讲可见性缩小为本类的内部,去除伴生对象。

class Student04 {
  protected[this] var id:Int=1
}
object Student04{
  def main(args: Array[String]): Unit = {
    var st=new Student04
    st.id =10//error
  }
}
class SmallStudent04 extends Student04 {
  def sayHello()={
    println(s"${id}")
  }
}

包限定

class Student05 {
  private[demo12] var id:Int=10
}
class SmallStudent05 extends Student05 {
  def sayHello():Unit={
    println(s"${id}")
  }
}

当使用包限定的时候,就打破了private|protected的语义,全部以包为准

final限定(掌握)

  • 修饰类:最终类,不可以被继承
  • 修饰方法:不能被覆盖
  • 修饰属性:只能修饰属性,不可以修饰局部变量(scala已经提供了val表示常量),当final修饰成员变量,属性不可以被遮盖。(scala中只用val的成员变量才可以被覆盖

sealed(密封|闭包)

Trait和class可以标记为 sealed,这意味着必须在同一源文件中声明所有子类型这样就确保了所有的子类型都是已知的。通常应用在case-class声明。

sealed  class User
case class SmallUser(name:String) extends User
case class BigUser(name:String,age:Int) extends User
var a=Array[Any](new SmallUser("小学生"),new BigUser("大学生",18))
val u = a(1)

u match {
    case SmallUser(name) => println(name)
    case BigUser(name,age) => println(name+" | "+age)
    case _ => println("未知生物")
}

lazy加载

Scala中使用关键字lazy来定义惰性变量,实现延迟加载(懒加载)。 惰性变量只能是不可变变量,并且只有在调用惰性变量时,才会去实例化这个变量。

def getName():String={
    println("----getName-----")
    "zhangsan"
}

lazy val name:String=getName()
println(name)

函数对象(lambda表达式)

函数式接口

事实上在Java 8中也引人了函数式接口,类似Scala中的函数式对象。例如在Java中定义一个函数式接口:

@FunctionalInterface
public interface GreetingService {
    String sayHello(String name);
}

要求接口中只能有一个方法声明。只有函数式接口才可以使用lambda表达式做匿名类实现。

GreetingService gs=(String name) -> "hello "+ name;
String results = gs.sayHello("zhangsan");
System.out.println(results);

类似的这种写法在Java 8的集合stream编程中非常常见。例如

List<String> lines= Arrays.asList("this is a demo","good good study","day day up","come on baby");

lines.stream()
    .flatMap(line->Arrays.asList(line.split(" ")).stream())
    .forEach(word->System.out.print(word+" | "));

更多有关Java8 lambda信息请参考:https://howtodoinjava.com/java-8-tutorial/

Scala这门语言在lambda编程的灵活性上是java无法媲美的。因为Scala在声明函数式对象(等价java函数式接口)是非常轻松的。

在这里插入图片描述

例如以上案例的GreetingService如果使用Scala声明可以简写为如下:

val sayHello:(String)=>String = (name) => "hello ~"+name
println(sayHello("zhangsan"))

部分应用函数

在Scala中同样对所有的函数都可以理解为是一个函数对象。例如在Scala中可以将任意一个函数转变成对象。例如如下定义一个sum函数。

def sum(x:Int,y:Int):Int={
    x+y
}

在Scala中可以尝试将一个函数转变成一个对象类型,如下:

scala> def sum(x:Int,y:Int):Int={
     |     x+y
     | }
sum: (x: Int, y: Int)Int

scala> var  sumFun = sum _
sumFun: (Int, Int) => Int = <function2>

scala> sumFun(1,2)
res0: Int = 3

通常将sumFun称为sum函数的部分应用函数,不难看出sumFun事实上是一个变量。该变量的类型是(Int, Int) => Int通常将该类型称为函数式对象。事实上以上的(Int, Int) => IntFunction2的变体形式。因为Scala最多支持Funtion0~22种形式变体。例如:

class SumFunction extends ((Int,Int)=>Int){
  override def apply(v1: Int, v2: Int): Int = {
    v1+v2
  }
}
等价写法:

class SumFunction extends Function2[Int,Int,Int]{
  override def apply(v1: Int, v2: Int): Int = {
    v1+v2
  }
}
scala> sumFun.isInstanceOf[(Int,Int)=>Int]
res2: Boolean = true

scala> sumFun.isInstanceOf[Function2[Int,Int,Int]]
res3: Boolean = true

isInstanceOf该方法等价于java中的instanceof关键字。用于判断类型。

PartitalFunction(偏函数)

偏函数主要适用于处理指定类型的参数数据,通常用于集合处理中。定义一个函数,而让它只接受和处理其参数定义域范围内的子集,对于这个参数范围外的参数则抛出异常,这样的函数就是偏函数(顾名思异就是这个函数只处理传入来的部分参数)。偏函数是个特质其的类型为PartialFunction[A,B],其中接收一个类型为A的参数,返回一个类型为B的结果。

val pf1=new PartialFunction[Any,Int] {
    override def isDefinedAt(x: Any): Boolean = {
        x.isInstanceOf[Int]
    }

    override def apply(v1: Any): Int = {
        v1.asInstanceOf[Int]
    }
}
val pf2:PartialFunction[Any,Int] = {case x:Int => x*2}
val a = Array(1,"a",2,"b",true,3)
//for(i<-a;if(i.isInstanceOf[Int])) yield i
a.collect(pf1)
a.collect(pf2) //偏函数变体写法

Scala中异常处理

java异常

在java编程中将异常分为 运行时异常和已检查异常。针对已检查异常需要用户声明式处理(try、throws)。现在我们回顾一下try处理。

try{
    int i=10/0;
    System.out.println("执行不到!");
}catch (ArithmeticException e){
    System.err.println("ArithmeticException");
}catch (Exception e){
    System.err.println("Exception");
}finally {
    System.err.println("finally");
}

java要求异常匹配由小->大。

scala异常

scala在编写过程中,不会区分异常是否是运行时异常和已检查异常,用户对任何异常都可以选择性处理。如果用户不处理异常会交给调用者处理。

try {
    val i = 10 / 0
    System.out.println("执行不到!")
} catch {//scala再捕获异常的时候采取的策略是顺序匹配
    case e: ArithmeticException =>
    System.err.println("ArithmeticException")
    case e: Exception =>
    System.err.println("Exception")
} finally {
    System.err.println("finally")
}

隐式传值\隐式转换

隐式变量

object CustomImplicts {
  implicit var name:String="张三"//隐式变量
}
object TestImplicit {
  def main(args: Array[String]): Unit = {
    import CustomImplicts._ 
    var s=implicitly[String]
    println(s)
  }
}

注意当前上下文中只能有一种类型的隐式变量,否则编译失败。

隐式参数

def sayHello(name:String)(implicit msg:String):Unit={
  println(s"${msg} ~ ${name}")
}

隐式转换

implicit def str2Date(s:String):Date={
    val sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
    sdf.parse(s)
}

def showDate(date:Date):Unit={
    println(date.toLocaleString)
}
showDate(new Date())
showDate("2019-8-8 14:44:27")//使用到了隐式转换

注意当前上下文中只能有一种隐式转换方法,否则编译失败。

隐式增强

implicit class PigImplicts(pig:Pig){
    def fly():Unit={
        println(s"${pig.name} 会飞~")
    }
    def swimming():Unit={
        println(s"${pig.name} 会游泳~")
    }
}

class Pig(var name:String) {
    def sleep():Unit={
        println(s"${name} 哼哼睡~")
    }
    def eat():Unit={
        println(s"${name} 给啥吃啥~")
    }
}
object Pig{
    def apply(name: String): Pig = new Pig(name)
}
var pig=Pig("猪坚强")
pig.eat()
pig.sleep()
//隐式增强
pig.fly()
pig.swimming()

Java中的类和Scala中类重名该如何区别

import com.baizhi.demo18.Pig         //scala
import com.baizhi.demo19.{Pig=>JPig} //java

var pig1=new Pig("猪坚强")
var pig2=new JPig

Java集合和Scala中集合相互转换(面试考点)

import java.util.{ArrayList => JList}
import scala.collection.JavaConverters._ //隐式增强

var list=new JList[String]()
list.add("hello word")
list.add("good good study")

val slist = list.asScala.toList
slist.flatMap(line=>line.split(" ")).foreach(w=>print(w+" | "))

泛型限定(了解)

Animal > Dog > SmallDog

T>:U下界限定

class Keeper[U >: Dog] {
}
var keeper=new Keeper[Dog|Animal]
var keeper=new Keeper[SmallDog]//错误

注意

Scala在定义方法泛型限定目前存在Bug。按照下届的定义,该方法只能传递Dog或者是Animal,不允许传递SmallDog,但是经过实战测试目前Scala在方法制定下届限定存在问题。

def keep[U >: Dog](u:U):Unit={
    println(u)
}
keep(new SmallDog)//这么写也对

T<:U上界限定

class Keeper[U <: Dog] {
}
var keeper=new Keeper[SmallDog|Dog]
var keeper=new Keeper[Animal]//error

T<%U视图限定

视图限定将会在后续版本移除,主要意思是指必须保证上下文中有能够提供一个隐式转换T <% U能够将T隐式转为U类型.如下系统会认定name看做成是User类型。

def sayHello[String <% User](name:String):Unit={
    name.sayHello()
}
implicit def str2User(s:String):User={
    new User(s)
}
sayHello("张三")

T:A上下文绑定

表示上下文中必须存在这种隐式值A[T]隐式值,否则程序编译出错.这样可以在上下文中还没有隐式值得时候确保方法能编译成功。例如:

def sayInfomation[T:Say](msg:T):Unit={
    var say=implicitly[Say[T]]
    say.sayHello(msg)
}

要求上下文中必须存在implict var s=new Say[T]

implicit  var say1=new Say[String]
sayInfomation("张三")

如果用户定义如下代码:

def sayInfomation[T](msg:T):Unit={
    var say=implicitly[Say[T]]//报错。需要在上下文中获取`implicit  var say1=new Say[String]`
    say.sayHello(msg)
}

+A协变

class Keeper[+Dog] {
}
var k1=new Keeper[Dog]
var k2=new Keeper[Animal]
var k3=new Keeper[SmallDog]
k1=k2 //error
k1=k3 

-A逆变

class Keeper[-Dog] {
}
var k1=new Keeper[Dog]
var k2=new Keeper[Animal]
var k3=new Keeper[SmallDog]
k1=k2
k1=k3 //error

A不变

class Keeper[Dog] {
}
var k1=new Keeper[Dog]
var k2=new Keeper[Animal]
var k3=new Keeper[SmallDog]
k1=k2 // error
k1=k3 // error

集合|数组

基础操作(创建、长度、元素)

Array(数组)
//=============Array============
var array=Array(1,2,4,3,5)//创建
var aitem=array(2)// 4
array(0) = -1 //修改下标元素
var alen=array.length //array.size
Range(区间)
//=============Range============
 //var range=Range(0,10,2)// start end step [0 2 4 6 8]
 var range= 0 until 10 by 2
 var ritem=range(2) // 4 ,range只读不允许修改
 var rlen=range.length //array.size
Vector(向量)
// =============Vector============
var vector=Vector(1,2,3,4)//表示对应位置的参数
var vitem=vector(2) // 4 ,range只读不允许修改
var vlen=vector.length //array.size
Iterator(迭代)
// =============Iterator============
var iterator=Iterator(-1,2,4,6,8) //只允许地迭代一次
var empty = iterator.isEmpty //判断是否为空
for(i <- iterator) println(i)
var ilen=iterator.length //iterator.size 一旦获取长度,迭代器失效
Seq(序列)
// =============Seq============
 var seq=Seq("a","b","d","c")
 var sitem = seq(0) //只读
 var newseq1= seq.++(Array("e","g"))  //将任意一个集合追加后面,并返回新的seq,不影响原始seq
 var newseq2= seq.++:(Array("e","g")) //将任意一个集合追加前面,并返回新的seq,不影响原始seq
 var newseq3= seq.+:("e")             //将任意一个元素追加前面,并返回新的seq,不影响原始seq
 var newseq4= seq.:+("e")             //将任意一个元素追加后面,并返回新的seq,不影响原始seq
 var slen = seq.length//seq.size  
List(不可变集合)
var list=List(1,5,3,4)
 var litem= list(0)    //该list元素不可修改元素
 var newList1=list.+:(-1) //前加 +: ::、:+ 后加 list不变
 var newList2= list.::(-1) //同上等价
 var newList3=list.++:(List(-1,-2))//前加++: 、后加 ++
 var newlist4=list.:::(List(-1,-2)) //前加
 var newlist5= list.drop(2)//删除前2个元素  后删 dropRight
 var newlist6 = list.dropWhile(item => item<5) //[5,3,4]
 var index = list.indexOf(5) //获取元素下标
 var lhead=list.head //第一个   1
 var llast=list.last //最后一个 4
 var ltail=list.tail // 出去第一个 剩余所有 [5,3,4]
 var lslice=list.slice(0,3)//选择一个子区间 [1,5,3]
 var lreverse=list.reverse //反转[4,3,5,1]
 var ltake=list.take(2) //拿前2 takeRight
ListBuffer(可变集合)
//=============ListBuffer============
var listBuffer=ListBuffer(1,5,3,4)
listBuffer.+=(-1) //后 += 前加 +=:  添加元素
listBuffer.++=(List(-1,-2))//后 ++= 前 ++=: 添加集合
listBuffer(0) = -1 //修改制定位置值
listBuffer.insert(0,-5) //从制定位置添加
var newlb=listBuffer.-(5) //表示删除元素5,但是不改变原始集合
listBuffer.-=(5) //表示删除元素5,改变原始集合
listBuffer.update(1,-1) //修改指定位置
listBuffer.remove(1)//删除制定位置元素
Set(不可修改)
Set(可修改)
HashMap(不可变)
HashMap(可变)

高阶计算(算子)

学习Scala集合算子意义?

[外链图片转存失败(img-ESiE4Lgq-1568861850286)(assets/1565341962632.png)]

map算子(映射)
scala> var list=List(1,2,3,4)
list: List[Int] = List(1, 2, 3, 4)

scala> list.map(item => item * item ) 
res111: List[Int] = List(1, 4, 9, 16)

scala> var words=Array("hello","world")
words: Array[String] = Array(hello, world)

scala> words.map(word=>(word,1))  //等价 words.map((_,1))
res113: Array[(String, Int)] = Array((hello,1), (world,1))

filter算子(过滤)
scala> var list=List(1,2,3,4)
list: List[Int] = List(1, 2, 3, 4)

scala> list.filter(item => item%2 ==0 )
res119: List[Int] = List(2, 4)

scala> list.filterNot(item => item%2 ==0 )
res120: List[Int] = List(1, 3)
faltten(展开算子)-了解
scala> var lines=List("this is a demo","good good study","day day up")
lines: List[String] = List(this is a demo, good good study, day day up)

scala> lines.map(line => line.split(" "))
res121: List[Array[String]] = List(Array(this, is, a, demo), Array(good, good, s
tudy), Array(day, day, up))

scala> lines.map(line => line.split(" ")).flatten
res122: List[String] = List(this, is, a, demo, good, good, study, day, day, up)

flatMap(转换并展开)
scala> var lines=List("this is a demo","good good study","day day up")
lines: List[String] = List(this is a demo, good good study, day day up)

scala> lines.flatMap(line => line.split(" "))
res123: List[String] = List(this, is, a, demo, good, good, study, day, day, up)

sort(排序算子)
scala> var lines=List("this is a demo","good good study","day day up")
lines: List[String] = List(this is a demo, good good study, day day up)

scala> lines.flatMap(line => line.split(" "))
res123: List[String] = List(this, is, a, demo, good, good, study, day, day, up)

scala> lines.flatMap(line => line.split(" ")).sorted
res124: List[String] = List(a, day, day, demo, good, good, is, study, this, up)

scala> var pair=List(("hello",1),("a",4),("demo",2))
pair: List[(String, Int)] = List((hello,1), (a,4), (demo,2))

scala> pair.sortBy(t=>t._1) // pair.sortBy(_._1)
res125: List[(String, Int)] = List((a,4), (demo,2), (hello,1))

scala> pair.sortBy(t=>t._2) // pair.sortBy(_._2)
res126: List[(String, Int)] = List((hello,1), (demo,2), (a,4))

scala> pair.sortWith((t1,t2) => t1._1.compareTo(t2._1) > 0) 
res135: List[(String, Int)] = List((hello,1), (demo,2), (a,4))

scala> lines.flatMap(_.split(" ")).sortWith((v1,v2)=> v1.compareTo(v2)<0)
res139: List[String] = List(a, day, day, demo, good, good, is, study, this, up)

reduce(汇总)

scala> var list=Array(1,2,3,4)
list: Array[Int] = Array(1, 2, 3, 4)

scala> list.reduce((v1,v2)=>v1+v2)
res140: Int = 10

folder

scala> var list=Array(1,2,3,4)
list: Array[Int] = Array(1, 2, 3, 4)

scala> list.fold(1)((v1,v2)=>v1+v2)
res147: Int = 11		

aggregate


scala> list.aggregate(0)((v1,v2)=>v1+v2,(v1,v2)=>v1+v2)
res148: Int = 10

scala> list.aggregate(0)(_+_,_+_)
res149: Int = 10

groupBy

scala> var lines=List("this is a demo","good good study","day day up")
scala> lines.flatMap(_.split(" ")).groupBy(w=>w).toList.map(t=>(t._1,t._2.size))
.sortBy(_._2)
scala> var orderitems=List(("zs",1,4.5),("ls",2,29.9),("zs",2,3.5))
scala> orderitems.map(t=>(t._1,t._2*t._3)).groupBy(t=>t._1).toList.map(t=>(t._1,t._2.aggregate(0:Double)((v1,v2)=>v1+v2._2,(v1,v2)=>v1+v2)))

scala> orderitems.map(t=>(t._1,t._2*t._3)).groupBy(t=>t._1).toList.map(t=>(t._1,t._2.map(_._2).reduce(_+_)))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值