第1章  概述

Scala是一个编译型的静态类型语言

Scala REPL:Read(读)、Evaluate(执行)、Print(打印)、Loop(循环)

 

第2章  处理数据:字面量、值、变量和类型

scala编译器将从赋值判断这个值的类型,这个过程称为类型推导

命名:Scala中的名字可以使用字母、数字和一些特殊的操作符字符

Scala不准许从高等级类型转换成低等级类型,但支持低等级类型转换成高等级,可以选择toType手动转换。

要对正则表达式完成更高级的操作,需要调用它的r操作符将字符串转换为正则表达式类型,返回Regex实例。

val input ="Enjoying this apple 3.141596 times today"

val pattern = """.* apple ([\d.]+) times.*""".r

val pattern(result)=input

//   模式 (输出的结果) = 输入

val res=result.toDouble

Scala类型层次体系:

Scala学习笔记 --《Scala学习笔记》梳理(二)

  • Any、AnyVal、AnyRef类型是scala类层次体系的根
  • AnyVal是值类型,表示数据的核心值,在堆中分配内存,或者可以作为JVM基本类型在栈中分配内存
  • AnyRef是所有其他类型的根,只能作为对象在堆中分配内存
  • Nothing是所有类型的子类型,它的存在提供一个兼容的返回类型。
  • NULL是所有AnyRef类型的子类型,为关键字null提供一个类型。
  • Char是一个文本单位,使用时用单引号(区分String)
  • Unit类型相当于void,通常用来定义函数/表达式

val data=()

 

元组

元组是一个包含两个或多个值的有序容器,所有这些值可以有不同的类型,元组的作用只是作为多个值的容器。

创建元组:

val info = (5,"zhang",true)//创建1

val red = "red"->"like"//创建2

访问元组:

val name = info._2

val reversed =red._2->red._1

 

第3章  表达式和条件式

表达式:是一个返回一个值的代码单元

"hello"

表达式块:

val amount = {val x=5*20;x+10}

表达式嵌套:

{val a = 1;{val b = a*2; {val c = b+4; c}}}//返回值是c

语句:不返回值的表达式,返回类型为Unit

if else语句块

if表达式

val result = if(false)"what does this return?"

result:Any=()

结果值没有指定,编译器只能推测确定最合适的类型,可能返回一个String或Unit,所以选择了根类Any(String和Unit的共同根类)

在Scala中不需要三元式,因为if else可以紧凑写在一行上

匹配表达式

val max = x > y match {

   case true => x

   case false => y

}

match 匹配表达式可以存在其他操作

val status = 500

val message = status match {

   case 200 => "ok"

   case 400 => { println("ERROR-1")

   "error"}

   case 500 => { println("ERROR-2")

   "error"}

}

模式替换式

val kind = day match {

  case "MON"|"TUE"|"WED"|"THU"|"FRI" => "weekday"

  case "SAT"|"SUN" =>"weekend"

}

通配模式匹配:

(1) 值绑定模式:case other =>

(2) 通配符换式:case_ =>

(3)用模式哨兵匹配:

case pattern if <Boolean expression> => <one or more expression>

case s if s!=null => println("hello world")

(4)指定模式变量:对传入的值的类型进行判断

case 定义:类型 => expression

for循环

for(x <- 1 to 7){println(s"Day $x:")}

yield返回一个表达式,表达式中的集合可以用其他for循环中作为迭代器:

for(x<- 1 to 7) yield {s"Day $x:"}

res

for(day <- res) print(day+",")

迭代器哨兵

迭代器哨位也称为过滤器,在for中使用if

for{

t<-quate.split(",")

if t!=null

if t.size>0

}{println(t)}

嵌套迭代器

for(x<-1 to 2 y<-1 to 3){println(s"($x,$y)")}

值绑定

val power = for(i<- 0 to 8; pow = 1<<i) yield pow

while 和 Do/While循环

 

第4章  函数

在Scala中函数是可重用的命名表达方式,函数可以参数化,可以返回一个值。

纯函数:

  1. 有一个或多个输入参数
  2. 只使用输入参数完成计算
  3. 返回一个值
  4. 对于相同的输入总返回相同的值
  5. 不适用或影响函数之外的任何数据
  6. 不受函数之外的任何数据的影响

def multiplier(x:Int,y:Int):Int = { x*y }

过程

过程是没有返回值的函数,Scala翻译器就会导出这个函数的返回值类型为Unit

无输入函数

如果函数有副作用,定义时九阴当加括号

def hi():String ="hi"

表达式块

def formatEuro(amt:Double) = f"$amt%.2f"//调用函数

formatEuro(3.4645)

尾递归优化

为了避免递归过程中不适用额外的栈空间,Scala编译器可以使用尾递归优化一些递归函数。利用尾递归优化函数,递归调用不会创建新的栈空间,而是使用当前函数的栈空间。

需要在函数定义前或前一行增加文本@annotation.tailrec标志尾递归优化。

@annotation.tailrec

def power(x:Int,n:Int,t:Int =1):Int = {

   if(n<1) t

   else power(x,n-1,x*t)

}

 

power(2,8)

Int = 256

嵌套函数

def max(a:Int, b:Int, c:Int) = {

   def max(x:Int,y:Int) = if(x>y) x else y //嵌套函数

   max(a,max(b,c)) //max(42,181,19) --> 181

}

用命名参数调用函数:不按顺序指定参数

调用参数:greet(name="Brown",prefix="Mr")

函数参数默认值:

def greet(name:String, prefix:String = "")=s"$prefix$name"

Vararg参数:

Scala支持vararg参数,可以定义输入参数个数可变的函数

要标志一个参数匹配一个或多个输入实参,在函数定义中需要该参数类型后面增加一个星号*

def sum(items:Int*):Int = {

  var total = 0

  for(i<-items) total+=i

  total

}

sum(10,20,30)

参数组:每个参数组分别用小括号分割

def max(x:Int)(y:Int) = if (x>y) x else y

val larger=max(20)(39)

类型参数:相当于泛型

def identity[A](a:A):A = a

val s:String = identity[String]("Hello")

val d:Double = identity[Double](2.717)

 

第5章  首类函数

高阶函数:map() 、 reduce()、filter()

高阶函数优点:具体处理细节,留给高阶函数框架万恒,调用者可以指定做什么,让高阶函数处理具体的逻辑流。

  1. 声明式编程
  2. 命令式编程

函数类型

用通配符为函数赋值

def double(x:Int):Int = x*2

val mDouble = double_  //相当于函数指针

val amount=mDouble(20)

def max(a:Int,b:Int) = if (a>b) a else b

val maximize:(Int,Int) => Int = max

//            输入     => 返回 = 调用函数

maximize(50,30)

编写函数字面量

//参数为函数

def safeStringOp(s:String, f:String => String) ={

  if(s!=null) f(s) else s

}

//传入函数

safeStringOp(null,(s:String) => s.reverse)

占位符语法:_

函数柯里化:

如果保留一些参数,可以部分应用这个函数,使用通配符替代其中的一个函数

def factorOf(x:Int)(y:Int) = y%x ==0

val isEven = factorOf(2)_

val z = isEven(32)

传名函数

偏函数:调用一个偏函数时,如果所使用的数据不能满足其中至少一个case模式,就导致Scala错误

用函数字面量块调用高阶函数

def safeStringOp(s:String,f:String => String) = {

   if(s!=null) f(s) else s

}

val timedUUID = safeStringOp(uuid, s => {

    val timed = s.take(24)

    timed.toUpperCase

})

函数可以采用这种方式将单独的代码块包围工具函数中:

  1. 管理数据库事务
  2. 重新尝试处理可能的错误
  3. 数据局部,全部或外部值

 

第6章  常用集合

List

val colors = List("red","green","blue")

colors.head

colors.tail

colors(1)

colors(2)

colors.foreach((c:String) => println(c))

val sizes = colors.map((c:String) => c.size)

val total = numbers.reduce((a:Int,b:Int) => a+b )

 

List[List[Int]] = List(List(1,3,5),List(2,4,6))

Nil:所有列表都有一个Nil实例作为终结点,迭代器可以通过比较当前元素Nil检查是否达到列表末尾

Nil实际上是List[Nothing]的一个单例实例。

创建一个新的空列表时,实际上会返回Nil而不是一个新实例

while(i!=Nil){print(i.head+",");i=i.tail}

val l:List[Int] = List()

l==Nil

m.tail == Nil

Cons操作符:Cons操作符::绑定元素,可以构建一个列表

val numbers = 1::2::3::4::Nil

 

val first = Nil.::(1)//插第一个元素

val second = 2::first//追加效果,在第一个元素前添加一个元素2

列表的算数运算:

p94~95

val f = List(23,8,14,21) filter (_ > 18)

val p = List(1,2,3,4,5) partition (_ < 3)

val s = List("apple","to") sortBy (_.size)

List是一个链表,由于在列表末尾增加项会改变这个列表,需要复制整个列表,并返回得到这个列表。

表映射操作:

  1. collect
  2. flatMap
  3. map

List(0,1,0) collect {case 1 => "ok"}//偏函数

List("milk,tea") flatMap(_.split(','))//使用一个给定的函数转换各个元素

List("milk","tea") map(_.toUpperCase)//转换各个元素

规约操作:

  1. max
  2. min
  3. product //列表中的数相乘
  4. sum

List(41,59,26).max

List(41,59,26).min

List(1,2,3).product

List(1,4,5).sum

 

val validations = List(true,false,true)

validations forall (_ == true)

p101

转换集合

  1. mkString
  2. toBuffer
  3. toList
  4. toMap
  5. toSet
  6. toString

p102

======================================

Set

val unique = Set(10,20,30,20,20,10)

val sum = unique.reduce((a:Int,b:Int) => a+b)

Map

val colorMap = Map("red" ->123,"green"->456)

val hashWhite = colorMap.contains("white")

for(pairs <- colorMap){println(pairs)}

======================================

Java和Scala集合兼容性

完成Java和Scala集合之间的转换

import collection.JavaConverters._

asJava     List(12,29).asJava

asScala    new Java.util.ArrayList(5).asScala

 

第7章  更多集合

不可变类型

可变类型

collection.immutable.List

collection.mutable.Buffer

collection.immutable.Set

collection.mutable.Set

collection.immutable.Map

collection.mutable.Map

val m = Map("APPL"->597,"MSFT"->40)

val n = m - "APPL" + ("GOOC"->521)//删除一个元素,并增加

构建可变集合的方法:

  1. 创建新的可变集合
  2. 从不可变集合创建可变集合

不可变集合List、Map和Set都可以用toBuffer方法转换为collection.mutable.Buffer类型,最后可以通过toList,和toMap变为不可变函数

  1. 使用集合构建器

      newBuilder指定集合元素的类型,调用构建器的result转换为集合

val b = Set.newBuilder[Char]

b+='h'

b++= List('e','l','l','o')

val helloSet = b.result

数组

是一个大小不可变,但是内容可变的集合

Array类型实际上只是Java数组类型的一个包装器,另外提供了一个高级特性:隐含类

val colors = Array("red","green","blue")

colors(0)

colors

val files= new java.io.File(".").listFiles

val scala = files map (_.getName) filter(_ endsWith "scala")

Seq和序列

Scala学习笔记 --《Scala学习笔记》梳理(二)

Stream:懒列表,访问元素时才增加相应的元素

Vector类型要以一个Array提供后备存储

val hi = "Hello," ++ "worldly" take 12 replaceAll ("w","W")

Stream

Stream类型是一个懒集合,由一个或多个初始元素和一个递归函数生成,第一次访问元素时才会把这个元素增加到集合中

流生成的元素会缓存,以备以后获取,确保每个元素只生成一次

使用递归生成新的流,可以用来无限生成新的元素。

def inc(i:Int):Stream[Int] = Stream.cons(i,inc(i+1))//1.使用cons会生成流

val s = inc(1)

def inc(head:Int):Stream[Int] = head #:: inc(head+1)//2.使用#::也会生成流

def to(head:Char,end:Char):Stream[Char] = (head > end) match {

    case true => Stream.empty

    case false => head #::to((head+1).toChar,end)

}

 

val hexChars = to('A','F').take(20).toList

一元集合

A)Option集合

      Option类型表示一个值的存在或是不存在

      Option可以安全替代null值,Option类型本身没有实现,而是依赖两个子类型提供的具体实现:

                                                     Some、None

var x:String ="Indeed"

var a = Option(x)//Some

 

x=null

var b = Option(x)//None

 

a.isDefined

b.isEmpty

//检测分母是否为0

def divide(amt:Double,divisor:Double):Option[Double] = {

   if(divisor == 0) None

   else Option(amt / divisor)//提供一种安全方法检测函数结果

}

headOption 检测空列表

安全的Option抽取操作:

  1. fold
  2. getOrElse
  3. orElse

B)Try集合

util.Try集合将错误处理转变为集合管理,它提供了一种机制来捕获给定函数中的错误

使用Try的错误方法:

flatMap、foreach、getOrElse、orElse、toOption、map

val t = util.Try(...)//使用方法

val input="123"

val result = Util.Try(input.toInt) orElse util.Try(input.trim.toInt)

result foreach {r =>println(s"Parsed '$input' to $r!")}

 

val x = result match {

  case util.Success(x) => Some(x)

  case util.Failure(ex) =>{

   println(s"Couldn't parse input '$input'")

   None

}

}

C)Future集合

      Future后台任务

import concurrent.ExecutionContext.Implicits.global

val f = concurrent.Future {println("hi")}

异步处理future

def nextFtr(i:Int = 0) = Future{

  def rand(x:Int) = util.Random.nextInt(x)

  Thread.sleep(rand(5000))

  if(rand(3) > 0) (i+1) else throw new Exception

}

同步处理future

concurrent.Await.result()

 

第8章  类

继承、多态、封装

class User

val u = new User

val isAnyRef = u.isInstanceOf[AnyRef]

类参数

      类参数可以用来初始化字段

class User(n:String){

  val name:String = n

}

val u = new User("zhangsan")

嵌套类

类定义中嵌套类,嵌套类除了可以访问自己的字段和方法,还可以访问其父类的字段和方法

类型参数的类

class Singular[A](element:A) extends Traversable[A]{

   def foreach[B](f:A=>B) = f(element)

}

 

val p = new Singular("Planes")

抽象类

抽象类生命但不定义字段和方法。如果一个类扩展了抽象类,但这个类没有标志抽象类,就必须提供这些字段和方法的实现。

abstract class Car{

  val year:Int

  val automatic:Boolean = true

  def color:String

}

 

class RedMini(val year:Int) extends Car{

   def color="Red"

}

 

val m:Car = new RedMini(2005)

匿名类

apply方法

apply方法有时是指它作为一个默认方法或一个注入方法,可以直接调用而不需要方法名

class Multiplier(factor:Int){

   def apply(input:Int)=input * factor

}

 

val tripleMe = new Multiplier(3)

val tripled = treipledMe.apply(10)

懒值

lazy,只是第一次实例化这些值时才创建。

如果要确保时间或性能敏感操作在类的生命期中只执行一次,懒值是一个很好的方法:

  1. 存储基于文件的属性
  2. 打开的数据库连接
  3. 不可变数据等信息

包装类

可以在任何位置使用import

批量导入:

import collection.mutable._

import colection.mutable.{Queue,ArrayBuffer}//批量导入

别名

  为了防止发生冲突,可以使用一个导入的别名,在局部命名空间中对某个类型重命名

import collection.mutable.{Map => MuMap}

包装语法

私密性控制:protected、private

最终类:final

 

第9章  对象、Case类和Trait

对象

是一个类类型,只能有不超过1个实例

对象会在首次访问时在当前运行的JVM中自动实例化(在访问之前,不会实例化)

纯函数

会返回完全由其输入计算得到的结果,而没有任何副作用,而且在引用方面是透明的

apply方法伴生对象

伴生对象是与类同名的一个对象,与类在同一个文件中定义。伴生对象和类可以认为是一个单个单元,所以它们可以互相访问私有和保护字段及方法。

:paste

class Multiplier(val x:Int){def product(y:Int) = x*y}

object Multiplier{def apply(x:Int) = new Multiplier(x)}//伴生对象模式,默认构造方法

 

:paste

object DBConnection {

   private val  db_url ="jdbc://localhost"

   private val  db_user="franken"

   private val  val_db_pass="berry"

   def apply()=new DBConnection

}

 

class DBConnection {

    private val props = Map(

       "url" -> DBConnection.db_url,

       "user" -> DBConnection.db_user,

       "pass" -> DBConnection.db_pass

)

   println(s"Created new connection for" + props("url"))

}

REPL的粘贴模式还有一个好处:对象和类会同时编译。除了对私有字段的特殊伴生访问

编译

Date.scala

object Date{

   def main(args:Array[String]){

     println(new java.util.Date)

  }

}

 

scalac Date.scala //编译

scala Date //使用

Case类

Case类是不可实例化的类,包含多个自动生成的方法。它包括一个自动生成的伴生对象,这个对象也有其自己的自动生成的方法。Case类对数据传输对象很适用,这些类主要用于存储数据。

case类有一些自己的方法:

apply、copy、equals、hashCode、toString、unapply

case类优点:利用case类不需要编写太多样例代码

Trait

trait是一种支持多继承的类,可以同时扩展多个trait

扩展第一个trait使用extends,扩展第二个trait使用with

class Page(val s:String) extends SageStringUtils with HtmlUtils{...}

多trait实现原理

编译器会创建多个trait的副本,形成一个“很高”的单列层次体系

编译到.class二进制文件时,实际上会扩展一个类,这个类又扩展另一个类,后者进一步扩展下一个类

例如:

class D extends A with B with C

==>

class D extends C extends B extends A

自类型

是trait注解,向一个类增加这个trait时,要求这个类必须有一个特定的类型或子类型

class A{def hi="hi"}

trait B{ self:A => override def toString = "B:"+ hi}//增强方法

class C extends A with B

new C()

 

第10章  高级类型

高层元组和函数字面量

val t1:(Int,Char) = (1,'a')

val t2:(Int,Char) = Tuple2[Int,Char](1,'a')

val f1:Int => Int = _+2

val f2:Int => Int = new Function1[Int,Int]{def apply(x:Int) = x*2 }

隐含类

提供一种类型安全的方法:"monkey-pathc", 动态修改现有的代码,为现有类增加新方法和字段

//隐含类

object ImplicitClasses{

   implicit class Hello(s:String){ def hello = s"Hello,$s"}//隐含类

  

   def test = {

      println("world".hello)

   }

}

//隐含值,隐含参数

object ImplicitParams{

   def greet(name:String)(implicit greeting:String) = s"$greeting, $name"

   implicit val hi="Hello"

  

   def test = {

      println(greet("Developers"))

  }

}

ImplicitParams.test

类型别名

为现有的类型创建一个别名,类型别名只能在对象、类或trait中定义

object TypeFun{

   //Int 类型别名

   type Whole = Int

   val x:Whole =5

   //Tuple类型别名

   type UserInfo = Tuple[Int,String]

   val u:UserInfo = new UserInfo(123,"zhangsan")

}

抽象类型:类型泛型

trait Factory[A]{def create:A}

trait UserFactory extends Factory[User]{def create = new User("")}

定界类型

def check[A<:BaseUser](u:A){ if(u.name.isEmpty) println("Fail")}//不能高于BaseUser

          A>:Customer  //不能低于

类型变化:指定一个类型参数如何调整以满足一个基类或子类型

  1. 协变类型:必要时自动转换成基类
  2. 逆变类型:必要时自动转换为子类型

case class Item[+A](a:A){ def get:A = a }

case class Check[-A] { def check(a:A) = { } }

相关文章: