第1章 基础
- 调用函数和方法
-
调用函数,以math包中的函数调用为例
import scala.math._ "_"相当于java中的*
pow(2, 4) 返回16.0。如果是scala开头的包,可以省略scala
-
方法,scala中没有静态方法
与之对应的是每一个类都有一个同名的伴生对象,定义在伴生对象中的方法可以直接调用
类的构造方法可以使用伴生对象中的apply方法
例如Array(1,2,3,4),其实调用的是Array.apply(1,2,3,4) 。
第2章 控制结构和函数
-
条件表达式
val b:Any = if (condition) 1 else "abc"
如果是if(condition) 1 <=> if (condition) 1 else () 返回类型是AnyVal
Unit用"()"表示。AnyVal继承Any
-
语句的终止,可以使用Breaks,最好是使用while循环,通过标志位来终止循环
-
高级for循环和for推导式
for循环中可以放多个生成式,每个生成式可以带一个守卫。
例子: for(i<- 0 to 3 if i%2!=0; j<- 1 to 10 if j !=4)
for循环中定义任意多的变量在循环中使用
例子:for(i<- 0 to 3; from = 4 - i; j <- from to 10)
for推导式
例子: val cc=for(i<- 0 to 3) yield i*2 返回cc:Seq[Int]=Vector(0, 2, 4, 6) cc的类型和for中的第一生成式有关
-
函数
函数定义,必须有=号。除非是递归调用的方式,不然不用声明返回值的类型
def sum(a:Int,b:Int) = { a + b } +方式是Int隐式转换成RichInt之后的方法
-
可变参数
可变参数 def sum(args: Int*) = { //方法体里面使用的时候args变成了Seq //如果是递归调用,直接将args传进去则会失败 sum(args.tail: _*)//将Seq转成可变参数传入 //tail的意思是除第一个元素后的序列 }
-
过程
不带=号,没有返回值,那就是过程
def process(i: Int){ //这就是一个过程 }
-
懒值,在使用的时候才进行初始化
lazy val b=//操作开销比较大的资源
-
异常捕获
try{ // }catch{ case e1:NPE => {} case _ => {} } //通过模式匹配的方式来捕获不同的异常
第3章 数组相关操作
-
定长数组
Array("a","b")相当于java中的String[]
Array
[String](10)
长度为10的定长数组访问元素arr(0)
-
变长数组:数组缓冲
ArrayBuffer("10","9")
val s = ArrayBuffer
[String]()
数组尾端添加或者删除值都可以,且是一个高效的操作
s += "abc" s ++= Array("g", "h") s.trimEnd(5) 尾端移除5个元素 Array转ArrayBuffer和ArrayBuffer转Array s.toArray.toBuffer
-
遍历数组和缓冲数组
for(elem <- elems) for (i <- 0 until elems.length) 遍历数组的索引 for(i <- 0 until (elems.length,2)) 调着遍历索引 for(i <- (0 until elems.length).reverse) 倒着遍历索引
-
练习,移除数组中第一个负数后面的所有负数
代码逻辑: def main(args: Array[String]): Unit = { //移除数组中第一个负数之后的所有负数 val ints = ArrayBuffer(1, -1, 1, 2, -3, 3, -2, -5, 0) var first = true //获取到所有的索引 val indexes = for (i <- 0 until ints.length if first || ints(i) >= 0) yield { if (ints(i) < 0) first = false;i } //将满足条件的元素放到数组的前端,因为indexes(j)的值大于等于j,所以往前移的过程中,后面的值不会被覆盖 for (j <- 0 until indexes.length) ints(j) = ints(indexes(j)) ints.trimEnd(ints.length-indexes.length) println(ints) }
-
创建多维数组
//创建规则的多维数组,3行4列的二维数组 val doubleArray = Array.ofDim[Int](3,4) //创建不规则的多维数组,10行,每行数组的长度不确定的二维数组 val arr = new Array[Array[Int]](10) for (i <- arr.indices) { arr(i) = Array[Int](i) } //i是形参,在for的代码块中是不允许变更的。下面这种方式是错误的 for (elem <- arr) { elem = new Array[Int](9) }//编译失败的代码 //因为没有apply方法适用,所以用new的方式创建
-
scala和java的互转
//集合的隐式转换,如果调用java的api需要传List<String> import scala.collection.JavaConversions.bufferAsJavaList val list=ArrayBuffer(1,2,3) list.add(5)//适用java集合的方法添加元素 //java List转scala的Buffer import scala.collection.JavaConversions.asScalaBuffer val list1 = new util.ArrayList[String]() list1 += "a" println(list1)
第4章 映射和元组
-
映射的创建,可以创建hash映射和树映射
创建不可变的映射 Map("a"->1, "b"->2) 创建可变的映射 scala.collection.mutable.Map("a" -> 1, "b" -> 2) 创建一个空的可变映射,需要指定映射的方式 scala.collection.mutable.HashMap[String,Int]()
-
获取映射中的值
map("a")直接获取的方式可能会报错 Exception in thread "main" java.util.NoSuchElementException: key not found: c 正确的调用方式,get()调用返回Option进行模式匹配Some或者None 或者getOrElse("a",3)给默认值
-
更新映射中的值
为可变map添加值
mm += ("b" -> 3) //如果key已经存在,则修改 mm -= "c" //移除对应的key mm ++= Map("d" -> 4) mm --=Seq("d","a")
-
迭代映射
for ((k,v) <- mm) 反转映射 val newmm=for((k,v)<- mm if k!="a") yield (v,k)
-
排序映射
scala默认的是Hash映射,如果想要使用树结构的映射,需要key有序。scala只有一种不可变的Map
val sortMap = scala.collection.immutable.SortedMap[Int,String](1->"c",-1->"d",5->"t",0->"y") 还有一种方式,就是使用java的TreeMap来实现有序的映射。 如果按照插入的顺序访问映射,使用scala.collection.mutable.LinkedHashMap
-
与java的互转
import scala.collection.JavaConversions.mapAsScalaMap import scala.collection.JavaConversions.mapAsJavaMap
-
使用模式匹配获取元组的值
//模式匹配的方式获取元组的值 val t = (1, 2, "a", "b") val (f, second, _, _) = t _只是一个占位符,不使用
-
拉链操作
Array("a","b").zip(Array(1,2)).toMap
第5章 类
-
简单的类和无参方法
//scala不使用这种定义方式 class Counter{ private var i =0 def increment(): Unit ={ i+=1 } def current=i } //改变状态的方法Increment //获取值的方法,去掉了() class Counter{ var age=0 } 编译的时候自动创建setter和getter方法 getter方法是age(),counter.age调用,省略了() setter方法是age_=(参数),counter.age_=(1)调用,也可以重新setter方法 class Counter{ private var privateAge:Int=0 def age=privateAge def age_=(): Unit ={ //set逻辑 } } 和java一样定义setter和getter方法
如果把属性声明成private,则scala编译成java程序的时候setter和getter方法都声明成private的
如果属性声明成val,只有getter方法生成
如果不想要setter方法和getter方法,属性声明成private[this]
-
私有字段的访问
private修饰符
class Counter{ private var privateAge:Int=0 def age=privateAge def age_=(): Unit ={ //set逻辑 } } 类中能访问对象的私有属性包括非this对象的私有属性 class Counter{ private var privateAge:Int=0 def age=privateAge def age_=(): Unit ={ //set逻辑 } def compare(other:Counterr){ other.privateAge也是能访问的 } } 如果只想当前对象能访问私有属性,则使用private[this]来进行修饰 如果private[otherCounnter]修饰Counter的私有属性,如果指定其他类的类名,那么其他类中也可以访问Counter对象的私有属性
-
scala类编程javaBean
@BeanProperty var name:String=_ 为属性增加注解,自动生成setName方法和get方法,变量不能声明成private 总共生成4个方法 name name_=(name:String) setName(name:String) get()
-
辅助构造器
class Counter(address:String){ private var privateAge:Int=0 privateAge = 1 @BeanProperty var name:String=_ def this(name:String,address:String){ this(address) this.name=name } def this(name:String,other:String,address:String){ this(name,address) this.name+=other } } 辅助构造器必须调用主构造器,第二个辅助构造器其实也是调用了主构造器 class Counter(val address:String,private var tt:String) 主构造器中可以使用修饰符val、var、private,如果不声明,默认是val 加了修饰符,参数会被声明成属性。 如果没有修饰符,方法中使用了参数,也会被声称属性。如果没有使用,只是访问,不会被声明成字段 禁止调用主构造器 class Counter private(address:String) 这样只能调用辅助构造器了嵌套类
-
嵌套类
嵌套类,类中可以嵌套类,函数中可以嵌套函数,对象中可以嵌套类
第6章 对象
-
单例对象,定义一个object就是一个单例对象,对象在首次使用的时候构造出来
-
伴生对象,伴生对象和伴生类可以互相访问私有属性,前提是声明在同一个源文件中
类访问伴生对象中的私有方法的时候,必须类名.方法名的方式调用,不能直接方法名调用,因为方法不在同一个域中
-
扩展类或特质的对象
abstract class AbstractNothing(des:String){ def a() def b() } object ImplNothing extends AbstractNothing("nothing") { override def a(): Unit = {println("aaa")} override def b(): Unit = { println("bbb") } } class Counter(address:String){ private var privateAge:Int=0 @BeanProperty var name:String=_ def this(name:String,address:String){ this(address) this.name=name } def this(name:String,other:String,address:String){ this(name,address) this.name+=other } } object ImplNothing2 extends Counter("address") { //只能访问到非private的属性和方法 ImplNothing2.name ImplNothing2.setName("a") }
-
枚举对象
//枚举对象 object EnumColor extends Enumeration { // val GREEN, RED, BLUE = Value //调用value方法返回一个Value的实例 val GREEN=Value(0,"abc") val RED=Value(1,"abc") val BLUE=Value(2,"abc") //为枚举实例定义id和name,如果没有定义id就是前面一个的id+1,如果前面没有那就从0开始 //name没有定义那就是GREEN作为name } object TestH{ def main(args: Array[String]): Unit = { val blue: EnumColor.Value = EnumColor.BLUE } }
第7章 包和引入
第8章 继承
-
类型检查和转换
if (blue.isInstanceOf[EnumColor.Value]) { val newBlue=blue.asInstanceOf[EnumColor.Value] } //如果要准确判断是不是一个类的对象 if (blue.getClass == classOf[Counter]) { } 最优的方法是使用模式匹配,优于类型检查和转换 blue match { case a: Counter => { } case _ => { } }
-
典型构造顺序和提前定义的问题
object Demo02 { def main(args: Array[String]): Unit = { val et = new Class2() val arr = et.env println(arr.length) //打印的结果是0 } } class Class1{ val arraySize=10 val env:Array[String]=new Array[String](arraySize) } class Class2 extends Class1{ override val arraySize=2 } 解释,先调用父类的构造方法,父类初始化arraySize,此时创建env,想要获取arraySize,调用方法arraySize(),但是这个方法已经被重写(重写了字段,就是重写了getter方法),这是调用子类的方法arraySize(),返回子类的arraySize,此时子类的arraySize还没有初始化成2。所有的int值,在分配 空间的时候都会被初始化成0,所以得到的数组长度为0 采用提前定义的方式来解决这个问题 先初始化子类中的某些字段 class Class2 extends { override val arraySize = 2 } with Class1{ }
-
scala继承层级
与java中基本类型对应的类和Unit类都是继承AnyVal 其他类都是继承AnyRef,相当于java中的Object AnyRef和AnyVal都是继承自Any
第9章 文件和正则表达式
- 后面补充
第10章 特质
-
特质的定义方法(特质的意思是混入,将特质中的方法混入到类或者对象中)
//定义一个有抽象方法的特质和具体实现方法的特质,抽象方法不用abstract,实现抽象方法不用使用override object Demo02 { def main(args: Array[String]): Unit = { val demo = new TestDemo demo.console() } } class TestDemo extends ImpLogger1 { def console(): Unit = { log("hello") } } trait Logger { def log(msg: String) def consoleLog(msg: String): Unit = { println("aaa") } } trait ImpLogger1 extends Logger { def log(msg: String): Unit = { //可以实现,也可以不实现特质中的抽象方法,必须要加override println(msg + "ImplLogger1") } } //定义一个特质对象,在对象上混入特质,前提是定义类的时候先混入特质。特质中的方法可以是空方法,但必须是实现方法,不能是抽象的。对象上混入特质时,可以使用过子特质混入,使用子特质的方法 object Demo02 { def main(args: Array[String]): Unit = { val demo = new TestDemo with ImpLogger1 demo.log("demo") } } class TestDemo extends Logger { def console(): Unit = { log("hello") } } trait Logger { def log(msg: String){} def consoleLog(msg: String): Unit = { println("aaa") } } trait ImpLogger1 extends Logger { override def log(msg: String): Unit = { //可以实现,也可以不实现特质中的抽象方法,必须要加override println(msg + "ImplLogger1") } }
-
特质的调用链,最右边是最小的,从最右边开始调用
object Demo02 { def main(args: Array[String]): Unit = { val demo = new TestDemo demo.console() } } class TestDemo extends ConsoleLogger with TimestampLogger with ExtendLogger { def console(): Unit = { log("hello ") } } trait Logger { def log(msg: String){} } trait TimestampLogger extends Logger { override def log(msg: String): Unit = { //可以实现,也可以不实现特质中的抽象方法,必须要加override super.log(msg+"TimeStampLogger ") } } trait ExtendLogger extends Logger { override def log(msg: String): Unit = { super.log(msg+"ExtendLogger ") } } trait ConsoleLogger extends Logger{ override def log(msg: String): Unit = { println("console:"+msg) } } 输出结果:(无法通过super.log知道会调用哪个方法,具体调用的方法根据混入特质的顺序来确定。如果要指定调用哪个特质的方法,super[ConsoleLogger].log(msg)指定超类型来进行调用) console:hello ExtendLogger TimeStampLogger
-
将特质当做富接口来使用
class TestDemo extends Logger {//混入了Logger特质之后都可以使用logger方法 def console(): Unit = { debug("hello1") warn("w") } } trait Logger { def debug(msg: String) { println(msg) } def info(msg: String) { println("INFO: " + msg) } def warn(msg: String) { println("WARN: " + msg) } def error(msg: String) { println("ERROR: " + msg) } }
-
特质中的字段
特质中的字段可以是抽象的也可以是具体的,初始化了的字段就是具体的。
-
特质的构造顺序,和特质方法的调用顺序是有区别的。
- 超类先构造
- 再构造特质,最后构造当前类
- 构造特质的时候,先构造父特质。如果多个特质共用一个父特质,父特质也只被构造一次
- 特质从左到右构造
特质的构造是初始化特质中的字段和语句,不是方法 extends 超类 with 特质1 with 特质2
-
特质可以约束只能混入某种类的子类
只要加上this: 超类 =>
下面都可以调用超类的方法,同时特质只能混入超类对应的子类中
trait Logger { this: Exception => def log() { println(getMessage)//可以直接调用Exception的方法,包括抽象方法 } def log2(){ println(getLocalizedMessage) } def debug(msg: String) { println(msg) } def info(msg: String) { println("INFO: " + msg) } def warn(msg: String) { println("WARN: " + msg) } def error(msg: String) { println("ERROR: " + msg) } }
-
特质只能混入包含特定方法的类
class TestDemo extends Logger { def getMessage:String={ "message" } def getLocalizedMessage:String={ "localizedMessage" } def console(): Unit = { log() log2() } } trait Logger { //指定必须要实现的方法 this: {def getMessage:String; def getLocalizedMessage:String} => def log() { println(getMessage) } def log2() { println(getLocalizedMessage) } def debug(msg: String) { println(msg) } def info(msg: String) { println("INFO: " + msg) } def warn(msg: String) { println("WARN: " + msg) } def error(msg: String) { println("ERROR: " + msg) } }
第11章 操作符
-
一般的操作符都是左结合的,例如+-*/,也有是右结合的操作符
a::b::Nil 相当于a::(b::Nil) 创建了一个包含b的列表,再将a拼到列表的头部 调用方式等价于 Nil.::(b)
-
类似函数的调用方法apply和update
val map = new scala.collection.mutable.HashMap[String,Int]() map("user1")=100//类似函数的调用方式f(args)=value,f非函数,其实调用的就是f.update(args,value) Map("app1"->200)//类似函数的调用方式,f(args),f非函数,其实调用的就是f.apply(args)
第12章 高阶函数
-
使用函数进行排序
"abc bc white hello a".split(" ").sortWith(_.length < _length) 按照字符串的长度进行排序,顺序排序
-
闭包函数
可以在变量不在作用域时被再调用
//定义一个函数,可以生成不同函数的函数 def mulBy(factor: Double) = (x: Double) => x * factor //生成一个乘以10的函数和一个乘以2的函数,传入mulBy的局部变量10被函数mulBy10继续使用,这就是闭包 val mulBy10 = mulBy(10) val mulBy2 = mulBy(2) 每个返回的函数都有自己的参数设置,mulBy就是一个闭包
-
柯里化
//拆解需要多个参数的函数 def mul(x: Double, y: Double) = x * y def mul1(x: Double) = (y: Double) => x * y val d = mul1(10)(11) val d1 = mul(10,11)
第13章 集合
-
序列
-
可变序列(IndexdSeq)
Vector(是ArrayBuffer的不可变版本,可以使用下标进行访问)和Range整数序列(只存储开始、结束、和步长)
-
不可变序列
ArrayBuffer
-
-
列表
List,列表要么是一个空列表Nil,要么是一个head加一个tail,tail也是一个列表。
-
可变列表
LinkedList,通过一个引用对其操作
var linkedList = mutable.LinkedList[String]() linkedList:+="list"//对可变链表进行操作 var cur = linkedList //用一个引用来遍历链表 //判断链表是否为空 while(cur != Nil) { //cur.elem获取当前元素 cur = cur.next//指向下一个元素 }
-
集set
scala中没有不可变的排序集,Set(可变)是没有插入序的(基于hash实现)。如果要插入序保持不变,使用(可变)LinkedHashSet,如果要对set进行排序,使用SortedSet(不可变,底层是由红黑树来实现的)。如果需要可变的有序集,必须使用java的TreeSet。
-
各种集合添加和删除元素的方法。快学scala.pdf(p126)
一般而言,+是将元素插入到无先后序的集合,+:和:+是将元素添加到有先后的集合的头部和尾部
例如:
var vec = Vector(1,2,3) // vec(0)=1//相当于update(0,1)的操作,因为是不可变的序列,不能修改 println(vec(0)) val vec2 = vec:+5 println(vec2) 即使Vector是不可变,vec添加5组成的是一个新的Vector,不改变原来的序列
-
总结集合的操作
- 向后:+或向前+:追加元素到有先后顺序的集合中
- +追加元素到无先后顺序的集合中
- 用-移除元素
- 用++和--来批量添加和移除元素
- 对于列表,优先使用::和:::
- 改值操作有+=、++=、-=和--=
- 对于集合,union、intersect、diff操作使用++、& 、--
- 尽量不使用++:、+=:和++=:
-
化简、折叠、拉链、迭代器、流操作
-
化简、折叠操作
//使用reduceLeft来统计总数,化简 val ll = List(1, 3, 4, 5, 7) val words = List("w", "w", "a", "b", "w", "a") val result: Int = ll.reduceLeft(_ + _) //相当于((1+3)+4)+5 …… val result2 = ll.reduceRight(_ - _) //相当于1-(3-(4-(5-7))) //折叠,柯里化的使用,第一个参数是初始值,第二个值是要传入的函数 val result3 = ll.foldLeft(0)(_ + _) //结果和化简的reduceLeft一样 //要统计单词的词频,并且将词频放到一个不可变Map中去 val emptyMap = Map[String, Int]() val wordCount = words.foldLeft(emptyMap) { (x, y) => x + (y -> (x.getOrElse(y, 0) + 1)) } println(wordCount) //使用/:符号来实现foldLeft操作,相当于words./:(map)(func) val charToInt = (Map[Char, Int]() /: "wwabwa") { //注意前面的特殊符号调用要使用()包起来 (x, y) => x + (y -> (x.getOrElse(y, 0) + 1)) } println(charToInt) val middleResult = (1 to 10).scanLeft(0)(_+_) println(middleResult) //结果Vector(0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55)。初始值0也是中间结果 //使用/:简写方式来做foldLeft的操作,因为有:,所以是后面的list调用前面的初始值
-
拉链操作
zip
zipAll,当两个集合长度不一样的时候pad补充。
zipWithIndex
//拉链操作 zip zipAll(可以为较短的集合补充默认值) zipWithIndex val ll = List(1, 3, 4, 5, 7) val words = List("w", "w", "a", "b", "w", "a") val word2IndexGroup = words.zipWithIndex //输出List((w,0), (w,1), (a,2), (b,3), (w,4), (a,5)) println(word2IndexGroup) val ll2words = ll.zipAll(words,10,"zip") //输出List((1,w), (3,w), (4,a), (5,b), (7,w), (10,a)) println(ll2words)
-
迭代器
iterator在scala中使用较少,有一种情况迭代器是比较适合的,Source.fromFile,当从文件中读取资源,内存不能存下资源的时候,可以使用迭代器的next。迭代器对集合而言是一个“懒”的替代品。
-
流
-
-
懒视图
-
与java集合互操作
-
线程安全的集合
暂时不去使用scala的并发包,
-
并行集合
暂时不使用scala的并发包
第14章 模式提配和样例类
-
当成switch使用,同时可以当成一个表达式使用,而不是语句
val aa = 10 var bb = 0 //当成switch语句使用 aa match { case 10 => bb = 1 case _ => bb = -1 } //和if一样当成表达式使用 bb = aa match { case 10 => 1 case _ => -1 }
-
守卫
bb = aa match { case 10 => 1 case _ if aa == cc => 0 case _ => -1 } 先匹配带守卫的case_语句,不满足的时候再匹配case_语句
-
模式中的变量
val i = 'a' val dd = i match { case '+' => 1 case '-' => 0 case ch if Character.isDigit(ch) => Character.digit(ch, 10) } 变量ch可以将i的获取,守卫和后面的语句都可以使用 import scala.math._ val dd = i match { case `Pi` => 1 case '-' => 0 case ch if Character.isDigit(ch) => Character.digit(ch, 10) } 当常量Pi和case的变量封装冲突时,不要直接使用case Pi,可以将Pi用反引号引起来,代表它是常量
-
类型模式
可以对表达式的类型进行匹配,使用这种方式可以取代isInstanceOf,asInstanceOf
val j:Any = "" val ee = j match { case a: Int => a case b: String => Integer.parseInt(b) case _: BigInt => Int.MaxValue case _ => 0 } //进行类型匹配的时候泛型类型是不能识别的,除了在Array[Int]可以识别 //case _: Map[String, Int] => 1 不使用 case _ : Map[_, _] => 2 case _ : Array[Int] => 1
-
匹配数组、列表和元组
-
匹配数组
val k =Array(0,1) k match { case Array(0) => println("只包含0的数组") case Array(0,_*)=>println("以0开头的数组") case Array(x, y) => println(x + " " + y + "包含两个元素的数组")//对于两个Int类型拼接,可以x+""+y的方式 case _=>println("someone") }
-
匹配列表
val l =List(0,1) l match { case 0 :: Nil =>println("只包含0的数组") case 0 :: tail =>println("以0开头的数组") case x :: y :: Nil =>println(x + " " + y + "包含两个元素的数组") case _ =>println("someone") }
-
匹配元组
val t=(2,9) t match { case (0,_)=>println("匹配元组的第一个元素为0且元组元素个数为2") case (x,_)=>println("匹配元组的第一个元素") case _=> }
-
-
提取器
匹配其实使用的是提取器方法unapply和unapplySeq,unapply提取的是固定长度的对象,比如tuple。unapplySeq提取的是可变长度的序列,可长可短,比如Array和List
-
变量声明中的模式
val (o, p) = (1, 2) val (i, u) = BigInt(10) /% 3 BigInt的方法,将商赋值给i,将余赋值给u //将Array中的第一、二个元赋值给变量,但是tuple不能使用_*。tuple是固定长度的 val Array(a,b,_*)=Array(0,1,3,4,5)
-
for表达式中的模式
import scala.collection.JavaConversions.propertiesAsScalaMap //将java的properties转成scala的map val props = new Properties() for ((k, v) <- props if k.startsWith("global")) {} for (("conf", v) <- props) {}//遍历k为conf的所有键值对
-
样例类
case class A(k1:String,k2:String) case class B(k1:String) val rr:Any=A("a","") rr match{ case A(k,_)=>println(k+"样例类A") case B(k)=>println(k+"样例类B") case _=>println("nothing") } //样例类自带setter方法和getter方法,apply方法和unapply方法
样例类的copy方法
rr.copy(k1="cc")//更改样例类对象的属性
-
case语句中的中置表示法
-
匹配嵌套结构
使用较少
-
Option类
//Option是对象 //Some是Option的样例子对象,None是Option的样例子对象 val map = Map("a" -> 3) map.get("a") match { case Some(x) => println() case None => println("空") }
-
偏函数
PartialFunction[A,B],A是偏函数的输入类型,B是偏函数的输出类型。偏函数并不是对所有数据都处理的函数,与之对应的是全函数,偏函数只处理能匹配上case类型的数据
//偏函数的应用 val f: PartialFunction[Char, Int] = { case '+' => 1 case '-' => -1 } //apply方法返回能匹配上的表达式 val i1 = f.apply('+') //isDefinedAt,判断能不能匹配上偏函数 val matched = f.isDefinedAt('-')
第17章 类型参数
-
协变和逆变
-
泛型定义在类名或者方法名的后面
协变[+B],Array[Person]定义的方法参数,传入Array[Student],编译会失败。定义class Pair
[+T]
(val first:T, second: T),协变的类型参数def f(arr: Pair[Person]),调用方法就可以传入f(Pair[Student]),Pair[Student]就是Pair[Person]的子类。
-
逆变,-T和协变相反
-
第21章 隐式转换和隐式参数
-
隐式转换
隐式转换函数就是implicit声明的带有单个参数的函数
implicit def int2Fraction(n: Int) = new TestImplicitClass(n) import com.test.Demo02._ //导入隐式转换函数 2.readSelf() class TestImplicitClass(value:Int){ def readSelf(): Unit ={ println(value) } }
隐式转换发生的条件
- 当表达式的类型和预期的类型不一致时,sqrt(str)
- 当对象访问一个不存在的方法时
- 不发生隐式转换编译能通过时,不发生隐式转换
-
隐式参数
当函数或者方法的参数带有implicit的时候,会找一个缺省值赋值给对应的参数,这个缺省值可以是当前作用域中implicit val和def修饰的变量和函数,也可以是伴生对象中的implicit val和def修饰的变量和函数。也可以是import 导入的implicit val和def修饰的变量和函数
object Demo01 { implicit val defaultValue = "demo01 value" } object Demo02 { import com.test.Demo01._ def readDefaultValue(b:String)(implicit v:String){ //def readDefaultValue(implicit v:String,b:String)会报错,implicit后面只能接一个参数 println(b+v) } def main(args: Array[String]): Unit = { readDefaultValue("hello") } }