scala学习笔记

Scala笔记

因为有个项目(网游服务器)用到了scala,第一次接触感觉这门语言非常犀利,所以把做项目的过程中积累的这个语言相关的知识点整理下做个笔记。内容不分先后顺序,前后基本没有逻辑,就是简单的知识点块。我之前是做java的,所以有些比较都是针对java。
先介绍几个重要链接:
scala官网 http://www.scala-lang.org/
scalaAPI http://www.scala-lang.org/api/current/index.html
scala中文社区 http://www.scala-china.net/discuz/forum.php

  • scala指南,只需要一个浏览器就能学习、试用scala:http://zh.scala-tour.com/#/welcome
  • 当导入一个包中所有的类或者符号时,你应该使用下划线(_)而不是像java中的星号(*)
    例如:import java.io._
  • class 和 object 的区别:
    object是class的伴随对象。 scala中没有static关键字,所以java中static类型的变量、方法都要放在scala的object里非static的放在class里。

  • 只有一个参数的函数可以使用下面这样的表达式来表示:
    df format now
    其实就是下面的这个冗长的表达式的简洁写法
    df.format(now)

  • Unit关键字类似于java/C/C++中的void
  • 右箭头‘=>’表明程序中存在一个匿名函数,箭头左边是匿名函数的参数列表,右边是函数体。
  • 定义函数时,有返回值的时候”=”是不能省略的。没有返回值的时候,无论有没有参数”=”都是可以省略的。
def testFunc1() {//没有返回值 默认为Unit 可以省略“=”
  ...
}

def testFunc2():Int = {//有一个整形的返回值,“=”不能省略
  ...
}
  • i<- 0 to length-1相当于 i<-0.to(length-1) scala中没有java中所谓的”数值类型”,所有的类型都是类,都可以调用对应的方法。
  • 不可变的常量用val,可变的变量用var来声明,使用def来声明函数。
    **注意:**def a = 1 ,这里是声明了一个函数返回一个固定值1,而不是定义了一个变量。
  • 以array(i)来使用数组的索引而不是array[I]。数组项目的取得array(i)和更新array(i) = x,其实是调用array.apply(i)和array.update(i, x)方法
  • Scala中的类可以拥有参数(传给了默认构造器)
  • 在Scala中,当一个类继承一个trait时,它就实现了这个trait的接口,同时还从这个trait中继承了所有的代码
  • Actor
    self、!(感叹号是发送消息的方法)和receive方法都是Actor类中的方法,“!”实现了多线程异步调用
  • AKKA
class Test extends Actor with ActorLogging{
    def receive={
      ...
}}
如果要在 Actor 中继续创建子 Actor,需要使用内置的 ActorContext(implicit) 对象
context.actorOf(Props[children], name = "children")
  • Map是对偶(算是键值对的另一个说法吧)的集合。->操作符用来创建对偶。
”Alice“-> 10 生成一个 (“Alice”, 10)
  • for yield,会将循环中每次yield后面的表达式的结果组成一个集合Vector(a,b,c,d….)
  • 设置for 循环步进
0 to (-10, -1)或者0 to -10 by -1
  • 如果你熟悉 Scala 的 loop 结构, 就会知道在 for 后的圆括号中还可以许更多的事情. 你可以加入 “if” 表达式,或别的语句, 比如下面的例子,可以组合多个 if 语句:
   def scalaFiles = 
     for(  
       file <- filesHere  
       if file.isFile    
       if file.getName.endsWith(".scala")  
     ) yield file 
  • yield 关键字的简短总结:
    针对每一次 for 循环的迭代, yield 会产生一个值,被循环记录下来 (内部实现上,像是一个缓冲区).当循环结束后, 会返回所有 yield 的值组成的一个集合.返回集合的类型与被遍历的集合类型是一致的.
  • .asOpt[String] 将Option强制类型转换成String
  • Option.getOrElse 如果没取到则给个默认值
  • x map .. 相当于 x.map{…}
    相当于把x中的所有数据遍历一遍,每一个值都执行大括号内的操作
    也可以取Future类型数据中的值

  • for循环中带左箭头

  for(i   ← start until start+10 ){
    //I从start值开始,到start+10结束
  }
  • 偏函数PartialFunction[A,B] A代表输入,B代表输出
    被包括在花括号内的一组case语句就是一个偏函数——一个并非对所有输入值都有定义的函数
  • reciveWithin 方法指定想要等待多少毫秒的时间,查过这个时间就会收到一个Actor.TIMEOUT对象
  • copy方法和带名参数
    样例类的copy方法创建一个与现有对象相同的新对象。可以使用带名参数来修改某些属性
  • += 可以加一个数值
    ++= 可以加一个数组
  • List最常用的操作符是发音为“cons”的‘::’。Cons把一个新元素组合到已有List的最前端,然后返回结果List。例如:
val twoThree = List(2, 3)
val oneTwoThree = 1 :: twoThree
  • 添加删除元素的二元操作符
    当以:为结尾时,调用翻转 前在后,后再前
val seq1 = Seq(1,2,3) //1,2,3
val seq2 = Seq1 :+ 4 //1,2,3,4
val seq3 = 4 +: Swq1 //4,1,2,3
  • 类和它的伴生对象可以互相访问私有特性(这是其它对象不具备的),但必须存在于同一个原文件中
  • 伴生对象:在java和C++中有些类既有实例方法或者变量(没static关键字),又有静态方法或者变量(有static关键字)的类。伴生对象就是把static类型的方法或者变量放到一个Object里,但不要static关键字
  • case class 使用样例类作为消息
  • actor的消息存在“邮箱中”,actor的recieve方法从邮箱中获取消息并传给自己的偏函数
    邮箱可能会被不与任何语句匹配的消息占满,所以添加case _语句来处理任意消息
  • actor 发送消息的方法是异步的,但Actor的receive函数是阻塞函数,等待结果。
  • 邮箱可能被那些不满于任何case语句匹配的消息沾满,可以添加一个case _语句,来处理任意的消息
  • Actor运行再一个线程中,会先接受一条消息,再接收下一条
  • recover :
    Future类型的值再使用map遍历的时候,可以在最后添加recover,因为Future返回的值除了正常值意外还会有异常。revover就是来捕捉这些异常的。
  • 使用半生对象的apply方法来构建对象
  • scala 没有switch
  • scala允许不定义类的实现 class C 等同于 class C{}
  • 使用了case关键字的类就是 case classes
    这种类编译器会自动做如下事:
    1. 创建同名构造器和apply方法,class() 取代了new class()
    2. 以参数名为名称创建构造函数的参数,如 class(a:Int,b:String)相当于有两个参数a,b class.a, class.b就能直接调用
    3. 构造了toString,hashCode,equals的实现
    4. 添加一个.copy方法,可以创建一个可修改的拷贝。class.copy(a=“a1”)
  • onComplete
    Future的onComplete方法,当Future结束时会被调用
  • sender ! 在Actor内部通过sender ! 传递结果,发完即忘
  • sender ?/!? 异步地获得一个Future类型的值
  • sender !! 阻塞地等待返回值
  • fatheractor.sunactor.forward(msg)
    父actor将msg信息传送到子actor
    在做路由、负载均衡、备份时非常有用
  • lazy
    lazy可以延迟初始化字段,加上lazy的字段会在第一次访问的时候初始化,而不是类初始化的时候,lazy非常适合于初始化非常耗时的场景。
  • pipeTo sender
    import akka.pattern.pepe
    Future类型的数据 使用pipeTo sender 来发送消息
  • Try{}match{case success()=>… case Failure()=>…}
  • List filter{…} take 100 filter是筛选条件,take是取前多少个值
  • 以函数为参数的函数:
val t = testDef(i=>”111”+i)
def testDef(body:(Int=>String))={
  body(123*3)
}

println(t) 结果是 “111369”
上面的i=>”111”+i 是一个匿名函数,在testDef中它的名字叫body.
body(123*3)相当于把123*3的值带入到了“ body”函数中
- 函数的可选参数

method(i:Int = 1, s:String)//i就是可选参数(不填的话就取默认值1),s就是必填参数
  • 每个Scala类都包涵一个主构造器和任意多个辅助构造器。
    辅助构造器名称为this。
    每个辅助构造器的开始都必须先调用之前已经定义的一个主构造器或辅构造器。
    如果没有定义主构造器,则默认有一个无参的主构造器。
  • 在scala中,你几乎可以在任何语法结构中嵌套任何语法结构,可以在函数中定义函数,可以在类中定义类。
  • 类型变量界定:
    上界 T<:Comparable[T] 就是要求T必须是Comparable的子类
    class Pair[T<:Comparable](val first:T, val second:T){} 下届类似
  • 视图界定 : 就是要求T不必须是Comparable的子类,如果能隐式转换也可以
    T<%Comparable[T] 表示可以被隐式转换成Comparable类及子类型
  • 类型约束:
    T =:= U T是否等于U
    T <:< U T是否是U的子类型
    T <%
val t @ a_val_that_very_long=3
case t @ (“a_val_that_very_long1" | “a_val_that_very_long2” | “a_val_that_very_long3”) => pringln(t)
  • import 重命名
    import java.util.{HashMap => JavaHashMap} 相当于把HashMap重命名为JavaHashMap
  • 密封类(带有sealed关键字)
    这样的类需要在match匹配中所有可能的选择
sealed trait PiMessage
case object Calculate extends PiMessage
case class Work(start: Int, nrOfElements: Int) extends PiMessage
case class Result(value: Double) extends PiMessage
case class PiApproximation(pi: Double, duration: Duration)
  • for和Future
for{
  a<-Future(xxx)
  b<-Future(xxx)
  c<-Future(xxx)
}yield{ 
  do(a)
  do(b)
  do(c)
}
//abc三个future全部返回后才会之行yield里面的操作
//可以用来将多个Actor的回应组合成一个单独的计算而不调用Await.reuslt

code1:
val milkFuture = future{ getMilk() }
val meatFuture = future{ getMeat() }
for{
  milk <- milkFuture
  meat <- meatFuture
} yield{ 
  println(milk)
  println(meat)
}

code2:
for{
  milk <- future{ getMilk() }
  meat <- future{ getMeat() }
} yield{ 
  println(milk)
  println(meat)
}
//这两段代码的区别是, code1中getMilk,getMeat是并行的,code2中,是按先后顺序之行!
  • for和Option
val opt1 = Option(10)
val opt2 = Option(20)
val opt3 = None
for{
  o1 <- opt1
  o2 <- opt2
  o3 <- opt3
}{
  println(o1)
  println(o2)
  println(o3)
}
//结果是“什么都不会打印!!!”,在for的第一个结构体中,如果有任何一个元素是None,则第二个结构体中的任何代码都不会执行,不执行,不!
  • Stream 的 #:: 就是cons方法
  • scala.split() 和 scala split , 什么时候可以去掉 “.” 和 “() ”
    如果一个方法只有一个参数,调用时可以省去. 和 ()
  • 数组使用[](java是 <>)来指明类型,用()(java是[])来指明索引(其实调用的apply方法)
  • 常用容器:Array(内容可以修改)、List(内容不能修改)、元组(最大长度22)、set(分immutable.Set和mutable.Set两种)、map(分immutable.Map和mutable.Map两种)
  • 数组长度
Array: val arry1 = Array[Int](12) val arry = new Array[Int](12) //前者数组长度为1,后者长度为12
  • scala编程 能不用var就不用,能不用mutable变量就不用!
  • require 和toString方法
class Rational(n:Int, d:Int){
  require(d!=0)//自动载入,无须import,创建对象不满足时会抛异常
  override def toString = n+”/“+d
}


  • 尽量用递归函数代替while循环
  • 函数字面量,就表示是一个函数。(3是一个整数字面量,其实就是一个整数)
    例如 (x:Int)=>x+1 这就是一个函数字面量
  • scala 允许使用“占位符”下划线”_”来代替一个或多个参数,只要这个参数值在函数定义中 只出现一次。
  • 闭包

指的是在函数体内调用了外部的变量,定义这个函数的过程是将这个自由变量捕获而构成的一个封闭的函数。 但这个自由变量变化时,scala的闭包能捕获到这个变化。 闭包用于创建函数的函数中,被创建的函数是闭包。
  • 重复参数
    参数类型与最后一个参数一致,def echo(arg0:String,arg1:Int ),数组做参数时可这样调用echo(“abc”,intarray:_)
  • 柯里化(curring)
    指将接受多个参数的函数,转化为接收 一个单一参数的函数并且返回 接受余下参数 且返回结果的新函数的技术。
  • 如果函数只有一个参数,可以用{}代替(). println{“123”}和println(“123”)是一样的
  • 传值参数和传名参数
  • def func1(func2:()=>Boolean){…} 传值参数调用时 func1( ()=>5>3 )
    def func1(func2: => Boolean){…} 传名参数调用时 func1(5>3)


    • 无参数方法:不带参数也没有 副作用的方法
    • == 和 eq neq

    java里
    String s1 = “abc”;
    String s2 = new String(“abc”);
    s1==s2 结果为false
    因为”abc”本身就是一个字符串常量对象,而第二句代码里其实生成了两个对象:”abc” 和 new String(xxx). 对于对象间使用”==“,表示对比是不是指向同一个对象。
    scala中
    ==会自动适应变量类型,如果是值类型,就对比值。如果是引用类型,就视为equals方法的别名。 如果想比较引用是否相等就需要用 eq 或者 neq

  • flatmap 和 map的区别
    假如一个List使用map后返回 List(1,2,3,List(4,5)) 那用flatmap后就会返回 List(1,2,3,4,5),flatmap返回的是扁平的list
  • 你可能感兴趣的:(scala)