Scala学习笔记

版权声明:本文为博主原创文章,转载请注明站点:blog.wpeace.cn https://blog.csdn.net/peace1213/article/details/61918706

本文主要是我在阅读快学Scala(Scala for the Impatient)中所记录的笔记,暂缺:十六、十九、二十二。神奇的Scala:面向表达式编程。


写文章不易,欢迎大家采我的文章,以及给出有用的评论,当然大家也可以关注一下我的github;多谢;

第一章:基础

1、声明变量:val name1,name2: String=”hello” val不可变变量,var可变。
2、常用类型:Byte、Char、Short、Int、Long、Float、Double、Boolean。但是不像Java这里是是实实在在的类,具有方法。
3、+-*/等操作符其实是方法:a + b类似于a.+(b).Scala中可以将方法调用的.省略:如1.to(10) 类似于1 to 10。
4、没有++操作符,因为Scala中大部分的算术类型都是不可变的如Int类型。
5、Scala.math中定义了很多方法,类似于静态方法。引入包后可以直接调用如:sqrt(4)。
6、Scala中没有static静态关键字,不过存在一个单例对象定义:object a{},该类中的方法可以直接通过类名调用,类似于静态方法。通常一个类(class)应有一个伴生对象(object)。
7、不带参数的Scala方法通常可以不使用括号如:”hello”.distinct。每行语句结束不需要;号,除非一行有多条语句。
8、Scala中可以重载操作符,如之前的+-*/等操作符都是重载的方法。另外还有个特例()操作符重载,通过在类中定义一个apply方法就可以重载如:”hello”(4)类似于Java中的”hello”.charAt(i)。

第二章:控制结构和函数

1、条件表达式:if(x>0) 1 else -1 或 if(x>0){ 1 }else{ -1}。此处1 else -1 是一个表达式,返回类型是两个分支的公共类型。
2、Unit就是Java中的void,可以用()代替。
3、块表达式:{}表示为块,也是表达式,其返回类型是块中最后一个表达式的值。
4、赋值:赋值语句的返回类型是Unit如:x=1的值是Unit,所以不能x=y=1这样进行赋值,除非x是Unit类型。在使用x={}进行赋值时也要注意最后一个表达式是否为赋值语句。
5、输入和输出:输入,直接调用readLine()或者readInt等。输出,print、println、printf(“%s”,”hello”)
6、Scala有与Java和C++一样的while和do循环形式。但for循环不一样
7、for循环简单语法:for(i <- 集合){},注意<-符号代表去让i变量遍历右边的集合。示例:for(i <- 1 to 10) 能取到10 for(i <- 1 util 10)不能取到10。
8、跳出循环:1、函数中可以使用return跳出函数 2、循环条件使用Boolean变量 3、使用Breaks对象中的break方法:

import scala.util.control.Breaks._
var sum = 0
breakable {
  for (i <- 0 to 10) {
   sum += i
   if (sum >= 10) break
 }
}

9、高级for循环之生成器:for(i <- 1 to 3; from=4-i; j<- from to 3 if(i!=j))print((10*i +j)+” “)//将打印 13 23 31 32 解释:第一层循环i,每次循环i的时候第二层循环j。其中循环中变量from可以省,if语句用于筛选,注意if前面没有分号。可以使用{}代替()来换行使用。
10、for循环与yield结合使用,则整个循环语句返回的结果是一个集合,当for循环中有两个生成器时返回结果使用第一个生成器兼容的格式:for(i <- 1 to 10) yield i % 3 //结果:scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 0, 1, 2, 0, 1, 2, 0, 1)
11、Scala与C++相同支持函数,函数定义格式为:def abs(x:Double)= if(x>=0) x else -x 。必须指定函数的参数类型,如果函数不是递归就不需要写出返回类型。如果函数需要多个表达式才能实现可以使用代码块{},块中最后一个表达式就是返回值。Scala函数中不建议使用return返回,你可以使用但不推荐。递归函数:def fac(n:Int):Int =if(n<=0) 1else n*fac(n-1)
12、默认参数:就是在函数定义时将参数赋值如:def decorate(str:String,left:String=”[“,right: String =”]”)left+str+right。调用该函数的时候可以只用给出str的参数值就行,其他两个参数会使用默认参数,也可以在提供参数时给出参数名赋值参数,可以不按照顺序。
13、变长参数:def sum(args:Int*)={},需要接受的是seq的参数,sum(2 to 10)是错误的。可以通过追加:_*来解决不能接受的问题:sum(2 to 10:_*)
14、过程:没有函数名后面等号的函数称为过程,返回的是Unit.
15、懒加载:lazy val words=初始化表达式 ,该变量只有在使用时才会调用初始化
16、异常:异常与Java的异常类似,但是不强制函数向上传递异常。

第三章:数组

1、定义定长数组:类似Java和C++中的数组。定义:val nums=new Array[Int](10),会初始化为0,取值:nums(0)、赋值:nums(0)=12
2、定义变长数组:类似于Java/C++中的list。需要引入ArrayBuffer,定义:val b=ArrayBuffer[Int]()。取值:b(1)、尾部追加:b+=1、b+=(1,2,3,5)、b++=Array(1,2,3)、插入:b.insert(index,(边长参数))、移除:b.remove(index,可省参数:删除多少个)
3、遍历:for(i<- 0 until (a.length,2)),以每两个元素进行遍历。或者直接for(elem<-a)
4、数组转换:for(elem<-a)yield 2*elem 返回与a同样类型的集合
5、常用算法:求和:a.sum、最大/最小:a.max/min、排序:a.sorted/sortwith、打印:a.mkString(” and “) mkString(“<”,”,”,”>”)
6、多维数组:val matrix=Array.ofDim[Int](3,4) 访问:matrix(row)(column)=12。可以创建不同维度的数组:val triangle=new Array[Array[Int]](10)

第四章:字典和元组

1、定义不可变的映射:val socers=Map(“alice”->10,”bob”->3)或者val socers=Map((“alice”,10),(“bob”,11)),定义好后就不能改变了
2、定义可变的映射:使用包mutable下面的Map:val socers=Map(“alice”->10,”bob”->3),或者使用HashMap
3、映射操作:获取映射中的值scores(bob),改操作如果不存在建会报错,一般使用socers.getOrElse(“bob”,0)存在就返回,不存在返回None。增改操作:scores(bob)=10,socres +=(“alice”->1,”bob”->30)。删除操作:socres-=”alice”。
4、对于不可变的Map可以通过重新赋值进行增改:val so=scores+(“bob”->1) 删:val so=scores-“bob”。
5、遍历映射:同时处理键和值 for((k,v)<-映射)处理k和v,遍历键:for(k<-scores.keySet),遍历值:for(v<-scores.values)。
6、已排序映射:SortedMap。按插入顺序访问所有键:LinkedHashMap。
7、元组:类似于Python中元组.如:(1,3.14,”bob”),可以通过_1/_2/_3来进行访问其组元。字符串中存在一个partition的来处理字符串生成元组。可以使用zip来合成元组。

第五章:类

1、简单类和无参方法:字段和方法默认是public的,字段必须初始化,类不用声明为public的,一个文件中可以包含多个类并且公有可见。方法调用时没有参数时可以不带(),一般对取值不带().

class Hello{
    private val value: Int=2
    def increment(){value+=1}
    def current=value
}

2、get和Set方法:scala中对每个字段都带有默认的get和set方法:value 和 value_=;当然你也可以像上面小结1一样自己定义相关的方法。
3、对于私有字段,如果本类中调用了相同类的对象可以直接使用。另外对于private[this] var value=0//该字段只能本对象使用,也称对象私有。
4、如果要使用Java中类似的Bean属性类,只需要将字段标注为@BeanProperty将会字段生成。
03
5、辅助构造器:类似于Java/C++中的构造器,但是有两点不同:1)所有辅助构造器的名字都为this,2)每一个辅助构造器都必须以一个先前定义的其他辅助或主构造器的调用开始。
6、主构造器是与类定义交织在一起定义的,如果没有参数则是默认的主构造器。如果带参数则在类名后的()中定义,如果使用表5.1中定义的参数,将在类中为字段,如果不包含val和var,没有方法使用的情况下改参数只用于语句访问,如果方法使用了就是对象私有字段。在new对象时会先调用主构造器,然后执行定义中的所有语句,最后执行new。
04

/**
  * Created by peace on 2017/1/12.
  */
object work5 {
  def main(args: Array[String]): Unit = {
    val a=new test(1)
  }

}
class test(val a:Int=0,b:Int=1){

  println(b)
  def this(ta:Int){
    this(ta,2)
    println("1")
  }
}
//输出:2、1

7、嵌套类:可以在任可语法中使用嵌套类,Scala中每个实例对象都有它自己的嵌套类,也就是说两个实例的嵌套类是不同的两个类。解决这个问题可以通过伴生对象和类型投影。

第六章:对象

1、单例对象:Scala中没有静态方法和静态字段,你可以用object这个语法来达到同样目的,object定义的就是单例对象。可以有属性和方法,可以通过名字直接使用功能。基本上具有类的所有属性甚至可以扩展其他类,但是主构造器不能带参数。
2、伴生对象:可以通过定义与类同名的伴生对象来实现像Java和C++中类即有静态方法和实例方法的类。伴生对象和该类可以相互访问私有特性,但是必须在同一个文件中。
3、object可以扩展类和特质,然后继承了该类或多个特质的特性。
4、对象的apply方法,通过在对象定义apply方法可以直接使用类名()来定义对象而不要使用new,只需要将apply方法来实现对象的new和返回该对象。
5、在Scala中程序必须从object对象的main方法开始。

第七章:包和引入

1、包和Java中的包类似,只是Scala中定义包的方式更多,可以使用{},可以文件顶部标记。
2、Scala中包的作用域更加前后一致,子包可以直接使用父包中的内容。
3、Scala中引入了包对象,包对象中可以定义方法,属性。
4、Scala中一样有包可见性,不同于Java中不带修饰词就是包可见,通过使用private[packageName] def description="aaa"+name.实现包可见性。
5、引入类似于Java中的引入,引入全部写法是 _不是*如:import java.awt._ 。更加灵活的是可以在任何地方都可以声明引入。
6、引入过程中可以改变类的名字,通过类似元组的方式:import java.util.(HashMap=>JavaHashMap).
7、类似于Java一样默认引入了java.lang.*包一样,Scala默认引入了java.lang.、scala.、Predef._。三个包。

第八章:继承

1、Scala继承方式与java一样使用extends关键词,而且也一样只能继承单个类。一样可以通过对类定义是使用final修饰来防止继承,不同的是Scala还可以使用final修饰方法和属性来防止重写该方法和属性。
2、在Scala中一个非抽象方法重写时必须使用override修饰符如:override def toString=getClass.geName+super.toString+”peace”。调用父类中的方法通过使用super关键词。
3、测试某个对象属于哪个类使用isInstanceOf方法,当对象是该类或者子类的对象时返回True.
03
4、Scala中也有protected修饰符,与java中的一样
5、子类的辅助构造器不能调用父类的构造器只能通过子类的主构造器进行调用形式如下:

class  Employee(name:String,age:Int,val salary:Double) extends Person(name,age)//通过子类的主构造器进行调用

6、重写字段
03
7、Scala也可以和java一样定义匿名子类。和java一样也有抽象类通过关键字abstract定义。重写抽象类的方法时不需要override。抽象类中还可以拥有抽象属性,抽象属性就是没有初始化的属性。
8、对象构造是顺序:父类构造器-子类构造器
9、Scala中基本类型和until类型都继承与Anyval类,其他类都是AnyRef的子类,而Any类是整个继承层级的根节点类似于java中的object类

第九章文件和正则表达式

1、读取文件:val source=Source.formFile("fileUrl","UTF-8"),生成Source对象。
读取文件:

val file=new File(filename)
val in=new FileInputStream(file)
in.read(bytes)

写入文本文件:

val out=new PrintWriter("numbers.txt")
for(i<- 1 to 100) out.println(i)

2、读取行函数:val lineIterator=source.getLines 返回所有行的迭代,转换成字符串:val contents=source.mkString。
3、序列化:Scala通过使用extends继承serializable特质来实现序列化
03
4、正则表达式:构造正则对象Regex直接使用r方法:val regx=”[0-9]+”.r。返回所有匹配的迭代器:val matchiterator=regx.findAllIn(“带匹配字符串”)。首个匹配项:findFirstIn,匹配字符串的开始部分:findPrefixOf。

第十章特质:接口

1、特质定义:使用关键字trait定义特质,其中的方法为抽象方法需要实现,不需要限定符abstract,重写时也不需要给出override关键字。

trait Logger{
def log(msg:String)
}

2、特质继承:继承使用和类继承一样的关键字extends,特质可以继承多个,使用with关键字连接就行。
3、特质中可以有具体实现的方法,java中的接口在Scala中可以当做特质来使用,也可以在new对象时继承特质:val acct=new Peolpe with Logger
4、特质中的字段可以是具体的也可以是抽象的。如果你给出了初始值那么字段就是具体的,实现该特质的类不是继承了该字段,而是类似于类定义时定义了字段。抽象字段必须在实现类中写出该字段。
5、类构造器的调用顺序:
03
6、特质还可以继承类,该特质被实现时实现类自动继承特质的超类,假如我们的类已经扩展了另一个类,就必须该类是特质超类的超类。
7、Scala中的特质最后被翻译成jvm中的接口

第十一章操作符

1、标示符:Scala中可以使用Unicode字符组成标示符,还可以使用所有的AscII字符,但是建议使用类似于java标示符的方式。
2、中置操作符:a 标识符b。如 1-2,1 to 10 其实都是方法调用。中置操作符可以用来重写操作符,重写操作符只需要在类中定义该操作符的方法。
3、一元操作符:a 标识符,等同于a.标识符()。一元操作符比较特殊的四个操作符:+、-、!、~可以作为前置操作符。赋值操作符:a+=b。
4、scala当中大部分操作符都是左结合的,除了以冒号(:)结尾的操作符合赋值操作符。如用于构造列表的::操作符就是右结合的:

val a=List(1,2,3,4)
val b=-1::0::a//实际上是按照这样的顺序-1::(2::a)。实际上右结合就是调用第二个参数的方法:a.::(0).::(-1)

5、类似于apply方法的update方法:表达形式:obj(arg1,arg2,..)=value,对应于调用obj.update(arg1,arg2,…,value)方法。apply方法常用于伴生对象中用来构造对象而不显示的使用new,而update方法常被用于数组和映射中改值使用。
6、apply方法的反向操作方法unapply,常用在对象中用于生成对象,但他的输入是一个对象然后通过这个对象构造对象:val author="wang he ping" val Name(first,last)=author.这里就是掉用了Name.unapply(author)来构造对象。一般unapply方法返回的是Option类型。

第十二章函数—函数即对象

1、Scala中函数是头等公民,就和数字一样可以作为变量一样作为参数和赋值给其他变量。如:val fun=ceil _就是将ceil函数赋值给了fun函数,函数后的_意味着确实是将这个函数赋值给了fun变量,而不是忘记了输入参数。该处fun的类型为(Double)=>Double 。接收并返回Double的函数。你可以调用像函数一样调用:fun(3.14),还可以将该函数传递给函数如:Array(3.14,1.42,2.0).map(fun).
2、匿名函数:(x:Double)=>3*x当然也可以将该匿名函数作为函数的输入参数或者赋值给其他变量:val triple=(x:Double)=>3*x
3、带函数参数的函数:定义如下:

def valueAtOneQuater(f:(Double)=>Double )=f(0.25)

调用方式为:valueAtOneQuater(ceil_)或valueAtOneQuater(sqrt)。只要传入的函数参数是(Double)=>Double 类型。而valueAtOneQuater函数的类型为((Double)=>Double )=>Double
4、返回函数的函数:定义如下:

def mulBy(factor:Double)=(x:Double)=>factor*x

mulBy将返回一个函数:(Double)=>Double,而mulBy自己的类型为(Double)=> (Double)=>Double。
5、参数推断,当你将一个匿名函数传递给函数或方法时,Scala会尽可能推断类型信息如:valueAtOneQuater((x)=>3*x)函数会推断出x的类型为Double。当只有一个参数时你还可以直接valueAtOneQuater(x=>3*x)。甚至如果参数在右侧只出现了一次你可以用_替换掉x:valueAtOneQuater(3*_)。这些简写方式只有在参数已知情况下才有效,一般只推荐在函数参数中使用。
6、一些有用的高阶函数:map(fun)对集合中的元素都应用fun函数,filter方法输出集合中满足特定条件的集合。等
7、闭包:闭包确保Scala中可以访问非局部变量,比如上面的mulBy中的factor即是这样一个变量,通过赋值不同的值不同的调用会不一样。
8、柯里化:将原本接收两个参数的方法或函数,转变为只接收一个参数并返回另外一个函数(以第二个参数为输入参数)的过程。如下:

def mul(x:Int,y:Int)=x*y
//柯里化
def mul(x:Int)=(y:Int)=>x*y
//柯里化调用
mul(x)(y)
//Scala支持更加简单的方式
def mul(x:Int)(y:Int)=>x*y

第十三章集合

1、集合都继承于iterable,与java不同映射也属于同一层继承关系。indexedSeq是数组的超类型。
2、Scala中有可变和不可变的集合,不可变的集合不可以改变但是可以通过操作生成新的不可变集合。
3、不可变序列:vector类似于数组但底层机构是树,不是线性的不过也支持快速的随机访问, Range表示的是一个整数序列,Range对象只存储起始值,结束值和增值。可变序列与java中的大体类似
4、列表:在Scala中列表要么是空的要么是一个head元素加上一个tail元素而tail元素又是一个链表,我的思路是:嵌套链表,以head开始tail嵌套。存在链表操作符::用于将两个链表合成新的链表如:9::List(4,2) 结果是List(9,4,2),head为9.在遍历时可以用迭代器进行遍历也可以直接通过递归。
5、可变列表与不可变列表类似,只是可以通过elem和next对头部和尾部进行修改。如 var cur=list cur.elem=0,cur.next=list2
6、用于添加或去除元素的操作符总结如下:
03
7、将函数应用于集合:集合中有一个map方法接收一元函数为参数然后对集合中所有元素进行处理。还存在接收二元函数为输入参数的方法如:reduceLeft,foldLeft等。
8、拉链操作:zip 用于将两个集合进行结合成一个集合,而且新生成的集合长度与两个集合中短的相同。zipwthIndex用于将集合和索引进行结合。
9、流式一个尾部被懒计算的不可变列表,只有当需要时才会计算。流操作符是#:。流的性质当你不用时流只计算了头元素,其他元素采用懒计算,只有用到时才计算。如下:

//采用#:生成流
def numsFrom(n:BigInt):Stream[BigInt]=n#::numsFrom(n+1);
def main(args: Array[String]): Unit = {
  val squares=numsFrom(1).map(x=>x*x)//懒计算
  println(squares)//这里只输出Stream(1,?)
  print(squares.take(5).force)//这里强制计算输出:Stream(1, 4, 9, 16, 25)
}

10、Scala中的集合可以与java中的集合进行互操作,同样也有线程安装的集合但不建议使用。
11、并行集合,Scala中提供了方便对大型集合方便的并行操作来加快速度,感觉有点类似于MapReduce。通过par方法产出当前集合的一个并行实现。如: for(i<-(0 until 100).par)print(i+" ")将不会按大小正常输出,由于底层用了多个线程进行并发计算。但是对于求和等操作会将多个线程的结果进行叠加: print(((0 until 101).toList).par.sum)将输出5050.

第十四章模式匹配和样例类

1、Scala中的switch通过 ch match{ case 条件 => 语句},每个case后不需要break,存在与default想类似的全匹配:_ 。另外可以加条件进行判断来进行处理以匹配更多。如:case _ if(Character.isDigit(ch))=> 语句
2、模式中的变量:在case关键字后面可以跟着一个变量名,那么匹配的表达式会赋值给那个变量,其实全部匹配就是一个变量只是变量名为。同样你也可以在守卫中使用变量。如case i if character.isDigit(i)=>语句
3、Scala中你也可以对表达式的类型进行匹配,如:但是不能匹配特定的泛型,如特定的Map类型

obj match {
    case x:Int=>x //其中x为变量,只能匹配整数并将obj赋值给x
    case s:String=>Integer.parseInt(s)//与x类似,只是此处为字符串
    case _:BigInt=>Int.Maxvalue//变量名为_,只能匹配obj为BigInt的类型
    case _ =>0//全部匹配
}

4、匹配数组、列表和元组:匹配时可以通过数组的个数,列表的头尾,元组的组成等进行匹配。
5、样例类是一种特殊的类,经常被用于模式匹配,样例类的创建方式很简单如下:

abstract class Amout
case class Dollar(value:Double) extends Amount//继承于Amount
case object Nothind extends Amout//单例也可以样例类

其中构造器中的每一个参数都为val(除非显示定义为var),在伴生对象中提供apply方法用于构造新对象,提供unapply方法让模式匹配可以工作。并且将生成toSting,equals,hashcode,copy等方法。这些都是在定义该样例类时自动生成的。
在模式匹配时可以将类型为Amount的对象和样例类进行匹配,然后参数会直接绑定然后直接用样例类中的变量如下:

amout math{
    case Dollar(v)=>"$"+v
    case Nothing =>" "
}

6、Option类型:Scala中Option[T]类型是用来处理java中null值类型的,Option有两个子类型一个为None,一个位Some[T]。比如map的get方法在java中可能返回为null而导致出现NullPointerException异常,而Scala中返回的是一个Option[T]类型当值不存在时为None,存在时返回Some(T)通过get方法可以获得map中的值。
7、偏函数:被包在花括号没的一组case是一个偏函数,一个并非对所有输入都有定义的函数,偏函数是PartialFunction[A,B]的一个实例,其中A为输入,B为返回类型。该类有两个方法,apply方法用于模式匹配,一个isDefinedAt从输入中匹配到则返回True。如下:

val f:PartialFunction[Chart,Int]={case '+' => 1;case '-' => -1}
f('+')//调用f.apply('+'),返回1
f.isDefinedAt('-')//返回true
f('0')//抛出MatchError

第十五章注解

1、Scala中注解和Java类似,可以为类,方法,字段,变量,和参数添加注解,同时也可以使用java中定义好的注解。Scala中还可以为主构造器,表达式,泛型参数添加注解。注解定义:注解类需要扩展Annotation特质。
2、针对java特性的注解:比如用@volatile来将字段标记为易失的,没有用volatile关键字,还有如:@transient,@strictfp等注解。以及特殊的接口:@cloneable,@remote等。
待补充其他的

第十七章泛型类型

1、Scala中也存在泛型的概率,与java不同Scala的泛型用方括号来定义如:

class pair[T,S](val first: T, val second: S)//泛型类
//调用:可以指定类型,也可以不指定
val p=new Pair[Any,Any](42,"peace")
val p1=new Pair(42,"peace")//推断类型为Pair[Int,String]
//泛型方法:
def getMiddle[T](a: Array[T])=a(a.length/2)

2、泛型类型限定:类似于java中的T extends comparable 。Scala中用 T<:Comparable[T] 表示。与java不同Scala中存在下界通过R >:T指定。
3、特殊的泛型:试图界定需要存在隐式转换如:class Pair(T<%Comparable[T])<%关系意味着T可以被隐式转换为Comparable[T]。上下文界定需要存在隐式值如:class Pair[T : Ordering],需要存在Ordering[T]的转换。
4、类型约束:T=:=U测试T是否等于U,T<:

第十八章高级类型

1、取得类型通过对象.type进行取得,可以获得运行过程中对象的实际类型。每个类的实例获得的类型是不一样的,但是每个实例的类型是该类自己的子类。每个对象的类型都指向自己实际对象,且只有唯一实例。

scala> class Peace
defined class Peace
scala> val p1=new Peace
p1: Peace = Peace@183ec003
scala> val type1 : p1.type=p1//将p1的类型复制给type1,必须注明类型为p1.type
type1: p1.type = Peace@183ec003
scala> import scala.reflect.runtime.universe.typeOf
import scala.reflect.runtime.universe.typeOf
scala> typeOf[p1.type]
res3: reflect.runtime.universe.Type = p1.type
scala> type1.toString
res4: String = Peace@183ec003
scala> p1.toString
res5: String = Peace@183ec003
//从上可知类型和引用指向同一个对象

2、单例类型:单例类型是单例对象的类型:如object Title ;Title.type就是单例类型,可以作为函数的参数等。
3、类型别名:可以使用type关键字创建一个复杂类型的简单别名,例如:

class Book{
    import scala.collection.mutable._
    type Index=HashMap[String,(Int,Int)]
    //这样就可以用Book.Index来代替HashMap[String,(Int,Int)]类型。
}

4、结构类型:是指一组关于抽象方法、字段和类型的规格说明。例如

def appendLines(target: {def append(str:Sring):Any}),lines:Iterable[String]){}
//其中target就是结构类型参数,传入的target必选满足具备append方法即可。

5、复合类型:T with R with X…但是需要T,R,X具有交集。中置类型:是带有连个类型参数的类型,如Map[String,Int]可以写成 String Map Int。存在类型:就是类似于java中南的通配符?。Scala类型如下图:
03
6、依赖注入,抽象类型,家族多态,高等类型这里不再累述。
7、type和class的区别:每个对象都有自己不同的type。每个对象都有自己的class,同一类的对象的class相同。

第二十章Actor

1、Scala中有特质Actor类似于java中的并发编程接口Runnable,当我们要实现不同的actor的只需要实现Actor特质 如下:

import scala.actors.Actor
class HiActor extends Actor{
    def act(){
    while(true){
    receive{
         case "Hi" =>println("Hello")   
     }
    }
}
}
//启动actor
val actor1=new HiActor
actor1.start()

其中act方法类似于java中的run方法。不同于java中的并发,Actor是针对消息进行活动的。其中的receive就是用于接收消息的。
2、发送消息:actor是一个处理异步消息的对象,你可以向某个actor发送消息,actor可以对该消息进行处理也可以向下传递给其他actor。发送消息的方式很简单如:常用样例类(case class className(参数))作为消息对象。

//actor对象 ! 发送的消息
actor1 ! "Hi"//消息接收者actor,发送的内容为"Hi"。发送的内容可以为任意对象

3、接收消息:发送到特定actor的消息会存放在“邮箱”中,receive方法从邮箱中获取下一条消息并将它传递给它的参数,该参数为一个偏函数:见14章。如:

receive{
    case Deposit(amount)=>...
    case withdraw(amount)=>...
}//大括号包着的就是偏函数,其中每个case的匹配项是样例类。

如果在receive方法调用时没有匹配的消息,则会阻塞。邮箱会串行化处理消息,这样的话就可以不用担心actor只占用的情况。actor可以安全修改自己的数据,但如果修改不同actor之间的数据时还是会有占用的情况。
4、actor向其他actor发送消息的方法:1、可以像全局的actor发送 2、actor可以构造指向其他actor的引用 3、可以在接收消息时同时接收向下传递的actor 4、actor可以发送返回消息给发送方。5、还可以通过通道进行传送消息。
5、同步消息和Future:actor可以发送一个消息并等待回复,用!?操作符就行。也可以使用future进行实现。
6、actor运行时会占用线程资源,对jvm的开销会很大。为了节省开销Scala提供了react模式处理消息来达到共享线程的目的。react与receive不同的是处理完消息后就会结束而且抛出异常(Nothing),相当于一个线程结束。如:

react {
    case Withdraw(amout)=>
        react{//执行到这里时第一个react就会退出,不会在存在act方法中
            case Confirm()=>
                println("confirming"+amount)
         }//执行完这里第二个react也会退出,act方法结束。
}
//注意在whilefor循环中使用react时,如果没有递归调用act方法,调用完react就会自动结束(因为react会抛出nothing的异常),当然也可以通过使用loop组合来循环调用react
def act(){
    loop{
     react {
        case Withdraw(amout)=>process(amout)
    }
 }
}
//条件调用模式:
def act(){
    loopWhile(sum>0){
     react {
        case Withdraw(amout)=>process(amout)
    }
 }
}

7、actor的生命周期:开始如start方法的调用,结束于:1、act方法的返回,2、act方法由于异常终止,3、act中调用了exit方法。
8、actor的设计:避免使用共享状态,不要再actor中调用其他actor的方法,不要阻塞actor,最好通过不可变的消息交互,尽可能使用react。

第二十一章隐式转换和隐式参数

1、隐式转换函数就是以implicit关键字声明的带有单个参数的函数,能将输入的参数类型转换为另一种类型如:implicit def int2Fraction(n:Int)=Fraction(n,1)将整数转换为分数。这样就在引入该隐式转换函数后就能够直接进行如下运算将:val result=3*Fraction(4,5)//将自动调用隐式转换将3变为分数
2、引入隐式转换:1、位于源或目标类型的伴生对象中的隐式函数。2、位于当前作用域可以单个标识符指代的;隐式函数。
3、隐式转换自动调用的规则:1、当表达式的类型和预期类型不同时。2、当对象访问一个不存在的成员时。3、当对象调用某个方法,传入的参数类型不对时。
4、隐式参数:函数或方法可以带有一个标记为implicit的列表,在调用函数时可以显示给出参数,如果不给出隐式参数就必须在作用域里有带有implicit定义的隐式值用于自动传入这些参数。
5、利用隐式参数进行隐式转换:主要用于隐式参数来对泛型进行隐式转换。
6、上下文界定、类型证明不在累述。

学习推荐

Twitter启动的Scala课堂:http://twitter.github.io/scala_school/zh_cn/
书籍:《Scala for the Impatient》《programming in Scala》

本文来自伊豚wpeace(blog.wpeace.cn)

展开阅读全文

没有更多推荐了,返回首页