1 None,Nothing,Null,Nil
None是一个object,是Option的子类型,定义如下
case object None extends Option[Nothing] {
def isEmpty = true
def get = throw new NoSuchElementException("None.get")
}
scala推荐在可能返回空的方法使用Option[X]作为返回类型。如果有值就返回Some[x](Some也是Option的子类),否则返回None,例如
Nothing是所有类型的子类,也是Null的子类。Nothing没有对象,但是可以用来定义类型。例如,如果一个方法抛出异常,则异常的返回值类型就是Nothing(虽然不会返回)
Null是所有AnyRef的子类,在scala的类型系统中,AnyRef是Any的子类,同时Any子类的还有AnyVal。对应java值类型的所有类型都是AnyVal的子类。所以Null可以赋值给所有的引用类型(AnyRef),不能赋值给值类型,这个java的语义是相同的。 null是Null的唯一对象
Nil是一个空的List,定义为List[Nothing],根据List的定义List[+A],所有Nil是所有List[T]的子类
2 移除三目运算
val num = if (a>b) 1 else 0
3 移除continue等控制语句
for(i<-1 to 10 if(i!=2) ) {
prinln(i)
}
4 break与breakable
break 直接抛出异常 Nothing,breakable是一个高阶函数。
breakable{
for(i<-1 to 10 ) {
if(i!=2) break()
}
}
5 函数定义
def 函数名 ([参数名: 参数类型], …)[[: 返回值类型] =] { 语句… return 返回值 }
- 函数声明关键字为def (definition)
- [参数名: 参数类型], …:表示函数的输入(就是参数列表), 可以没有。 如果有,多 个参数使用逗号间隔
- 函数中的语句:表示为了实现某一功能代码块
- 函数可以有返回值,也可以没有
- 返回值形式1: : 返回值类型 =
- 返回值形式2: = 表示返回值类型不确定,使用类型推导完成
- 返回值形式3: 表示没有返回值
- 如果没有return ,默认以执行到最后一行的结果作为返回值
val f = (x:Int, y:Int) => {
x+y
}
6 递归函数必须显示指定返回类型
7 惰性函数
8 类构造器
1 可以有任意多个
2 一个主构造器,直接带形参
class A ( w:Int) {
var a:String =_
var b:Int= _
}
注意:1 w没有被val修饰,只是个局部变量
2w被val修饰,w变成只读的成员变量
3 辅助构造器,必须调用主构造器
4 @BeanProperty
5 主构造器才能调用父类的构造器
9 类成员 var类型不想赋值
class A {
var a:String =_
var b:Int= _
}
10 包
1 自动import java.lang.* , scala, PreDef包
2 包定义的形式 下面1与2完全一样、支持包嵌套
//1
package com.chenyixin.Test
//2
package com.chenyixin.Test
package Test
//3
package com.chenyixin{
package Test {
class TestA {
}
object TestA {
}
package w {
class TestA2 {
}
}
}
}
3 可见性
属性默认是private,方法默认是public
protect 只能子类访问,同包不能访问
没有public关键字,private[另一包名]]类似友元
4 import包
import可以出现在任何位置。
_代表java里面的*
import重名类,防止冲突 =》alis
import有冲突,防止冲突 =》_隐藏掉
11 伴生对象的apply方法
在伴生对象中实现apply方法,不用new对象
12 特质
1 特质用with实现,接口和抽象类,抽象方法编译成接口,不抽象编译成类
2 动态混入特质,更容易扩展。实际匿名类
3 叠加特质 声明顺序为从左向右(构造也是这个顺序),方法从右向左执行
4 调用父特质的方法 super[父类名]
5 重写父特质的抽象方法,且调用了super的该方法。必须继续声明abstract。这个有什么用呢,动态混入特殊顺序有密切关系。
5动态混入的特殊字段是动态加入的,不是继承的。
6 特质可以继承一个类。Java接口不能继承一个类,抽象类能继承。
7 自身类型。防止循环依赖
13 内部类
1 静态内部类放入伴随对象里面去
2 非静态类放入类内部
3 在外部new内部类, val object = Inner.Outer();与java相反
4 类的投影,将不同内部类,投影成一个。屏蔽外部类的对象对内部实例的影响。
14 隐式转换(重要)
1 隐式转换函数是以implicit关键字声明的带有单个参数的函数。这种函数将会 自动应用,将值从一种类型转换为另一种类型
//编写一个隐式函数转成 Double->Int 转换
//隐式函数应当在作用域才能生效
implicit def f1(d:Double): Int = { //底层 生成 f1$1
d.toInt
}
implicit def f2(f:Float): Int = {
f.toInt
}
//这里我们必须保证隐式函数的匹配只能是唯一的.
// implicit def f3(f1:Float): Int = {
// f1.toInt
// }
val num: Int = 3.5 // 底层编译 f1$1(3.5)
val num2: Int = 4.5f //
println("num =" + num)
println("num =" + num2)
2 相同作用域只有一个能匹配。
3 隐式值。隐式值也叫隐式变量,将某个形参变量标记为implicit,所以编译器会在方法 省略隐式参数的情况下去搜索作用域内的隐式值作为缺省参数
implicit val str1: String = "jack~" //这个就是隐式值
//implicit name: String :name就是隐式参数
def hello(implicit name: String): Unit = {
println(name + " hello")
}
hello //底层 hello$1(str1);
4 隐式类。在scala2.10后提供了隐式类,可以使用implicit声明类,隐式类的非常强大, 同样可以扩展类的功能,在 集合中隐式类会发挥重要的作用。 隐式类使用有如下几个特点:
- 其所带的构造参数有且只能有一个
- 隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类 不能是 顶级的(top-level objects)。
- 隐式类不能是case class
- 作用域内不能有与之相同名称的标识符
//DB1会对应生成隐式类
//DB1是一个隐式类, 当我们在该隐式类的作用域范围,创建MySQL1实例
//该隐式类就会生效, 这个工作仍然编译器完成
//看底层..
implicit class DB1(val m: MySQL1) { //ImplicitClassDemo$DB1$2
def addSuffix(): String = {
m + " scala"
}
}
//创建一个MySQL1实例
val mySQL = new MySQL1
mySQL.sayOk()
mySQL.addSuffix() //研究 如何关联到 DB1$1(mySQL).addSuffix();
implicit def f1(d:Double): Int = {
d.toInt
}
def test1(n1:Int): Unit = {
println("ok")
}
test1(10.1)
}
}
class DB1 {}
class MySQL1 {
def sayOk(): Unit = {
println("sayOk")
}
}
5 隐式的转换时机
- 当方法中的参数的类型与目标类型不一致时
- 当对象调用所在类中不存在的方法或成员时,编译器会自动将对象进行 隐式转换(根据类型)
6 隐式操作不能嵌套使用
7 隐式解析机制
- 首先会在当前代码作用域下查找隐式实体(隐式方法、隐式类、隐式对象)。 (一般是这种情况)
- 如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。 类型的作用域是指与该类型相关联的全部伴生模块,一个隐式实体的类型T它的 查找范围如下(第二种情况范围广且复杂在使用时,应当尽量避免出现): a) 如果T被定义为T with A with B with C,那么A,B,C都是T的部分,在T的隐式解析 过程中,它们的伴生对象都会被搜索。 b) 如果T是参数化类型,那么类型参数和与类型参数相关联的部分都算作T的部分, 比如List[String]的隐式搜索会搜索List的伴生对象和String的伴生对象。 c) 如果T是一个单例类型p.T,即T是属于某个p对象内,那么这个p对象也会被搜索。 d) 如果T是个类型注入S#T,那么S和T都会被搜索。
15 高阶函数(重要)
15.1 定义函数执行与不执行
val print = printHello
val print2 = printHello _
print2()
}
def printHello(): Unit = {
println("hello")
}
15.2 定义高阶函数
// 1
val f =toDuble _
printHello2(f)
//2
val f2 = (x:Int)=>x.toDouble
printHello2(f2)
//3
printHello2(toDuble)
def printHello2(f:Int=>Double): Unit = {
println("hello"+f)
}
def toDuble(f:Int): Double = {
f.toDouble
}
16 化简
化简:将二元函数引用于集合中的函数,
val list = List(1,7,3,6)
def sum(int1: Int,int2: Int): Int = {
int1+int2
}
val sumvalue =list.reduceLeft(sum)
val sumvalue2 =list.reduceLeft((a,b)=>a+b)
val sumvalue3 =list.reduceLeft(_+_)
println(sumvalue)
16 折叠
/: : 简写
17 扫描
对某个集合的所有元素做fold操作,但是会把产生的所有中间结果放置 于一个集合中保存
def minus( num1 : Int, num2 : Int ) : Int = {
num1 - num2 }
//5
def minus( num1 : Int, num2 : Int ) : Int = { num1 - num2 }
//5 (1,2,3,4,5) =>(5,4,2,-1,-5,-10)
val i8 = (1 to 5).scanLeft(5)(minus)
18 拉链
对偶元组的合并,输出每个scala.Tuple2的集合
val list1 = List(1,2,3)
val list2 = List(4,5,6)
println(list1.zip(list2))
输出 List((1,4), (2,5), (3,6))
19 View
产生懒加载的集合,运行才执行
val viewSquares2 = (1 to 100)
.view
.map(multiple)
.filter(eq)
println(viewSquares2)
20 支持操作符重载
前置、中置、后置
21 Match 更强大的case
1 _作为未匹配到
2 不用break中断
3 用case _ if 支持范围匹配
val oper ="";
oper match {
case "1" => list1.apply(1)
case _ if (oper=="2") => list1.apply(2)
case _ =>println(1)
}
3 用case 类型匹配
oper match {
case a:Int => list1.apply(1)
case b:Double => list1.apply(2)
case _ =>println(1)
}
4 匹配数组
- Array(0) 匹配只有一个元素且为0的数组。
- Array(x,y) 匹配数组有两个元素,并将两个元素赋值为x和y。当然可以依次 类推Array(x,y,z) 匹配数组有3个元素的等等…
- Array(0,_*) 匹配数组以0开始
5 匹配列表
for (list <- Array(List(0), List(1, 0), List(0, 0, 0), List(1, 0, 0))) {
val result = list match {
case 0 :: Nil => “0”
case x :: y :: Nil => x + " " + y
case 0 :: tail => “0 …”
case _ => “something else” }
6 匹配元组
for (pair <- Array((0, 1), (1, 0), (1, 1),(1,0,2))) {
val result = pair match {
case (0, _) => “0 …”
case (y, 0) => y
case _ => “other” }
7 对象匹配
case中对象的unapply方法(对象提取器)返回Some集合则为匹配成功,返回none集合则为匹配失败
object Square {
def unapply(z: Double): Option[Double] = Some(math.sqrt(z))
def apply(z: Double): Double = z * z }
// 模式匹配使用:
val number: Double = 36.0
number match {
case Square(n) => println(n)
case _ => println(“nothing matched”)
}
8 变量声明中的模式
val (x, y) = (1, 2)
val (q, r) = BigInt(10) /% 3 //说明 q = BigInt(10) / 3 r = BigInt(10) % 3
val arr = Array(1, 7, 2, 9)
val Array(first, second, _*) = arr // 提出arr的前两个元素
9 for表达式中的模式
val map = Map(“A”->1, “B”->0, “C”->3)
for ( (k, v) <- map ) {
println(k + " -> " + v)
}
//v match 0
for ((k, 0) <- map) {
println(k + " --> " + 0)
}
22 样例类
为模式匹配而生
- 在样例类对应的伴生对象中提供apply方法
- 提供unapply方法让模式匹配可以工作
- 将自动生成toString、equals、hashCode和copy
23 Sealed类
密封就是不能在其他文件中定义子类
24 偏函数
- 在对符合某个条件,而不是所有情况进行逻辑操作时
- 将包在大括号内的一组case语句封装为函数,只对会作用于指定类型的参数或指定范围值的参数实施计算,超出范围的 值会忽略(未必会忽略,这取决于你打算怎样处理)
- 偏函数在Scala中是一个特质PartialFunction
val list = List(1, 2, 3, 4, "hello")
//定义一个偏函数
//1. PartialFunction[Any,Int] 表示偏函数接收的参数类型是Any,返回类型是Int
//2. isDefinedAt(x: Any) 如果返回true ,就会去调用 apply 构建对象实例,如果是false,过滤
//3. apply 构造器 ,对传入的值 + 1,并返回(新的集合)
val partialFun = new PartialFunction[Any,Int] {
override def isDefinedAt(x: Any) = {
println("x=" + x)
x.isInstanceOf[Int]
}
override def apply(v1: Any) = {
println("v1=" + v1)
v1.asInstanceOf[Int] + 1
}
}
//说明:如果是使用偏函数,则不能使用map,应该使用collect
val list2 = list.collect(partialFun)
println("list2" + list2)
简化方式1
def partialFun2: PartialFunction[Any,Int] = {
//简写成case 语句
case i:Int => i + 1
case j:Double => (j * 2).toInt
}
val list = List(1, 2, 3, 4, 1.2, 2.4, 1.9f, "hello")
val list2 = list.collect(partialFun2)
println("list2=" + list2)
简化方式2
val list = List(1, 2, 3, 4, 1.2, 2.4, 1.9f, "hello")
val list2 = list.collect{ case i: Int => i + 1 }
25 匿名函数
无名字,不定义返回值
val f = (x:Int)=>x+2
def minusxy(x: Int) = {
(y: Int) => x - y //匿名函数
}
//分步执行
//f1 就是 (y: Int) => 3 - y
val f1 = minusxy(3)
println("f1的类型=" + f1)
println(f1(1)) // 2
println(f1(9)) // -6
//也可以一步到位的调用
println(minusxy(4)(9)) // -5
26 参数推断
- 参数类型是可以推断时,可以省略参数类型
- 当传入的函数,只有单个参数时,可以省去括号
- 如果变量只在=>右边只出现一次,可以用_来代替
val list = List(1, 2, 3, 4)
println(list.map((x:Int)=>x + 1))
println(list.map((x)=>x + 1))
println(list.map(x=>x + 1))
println(list.map(_ + 1))
val res = list.reduce(_+_)
27 闭包
28 柯里化
函数编程中,接受多个参数的函数都可以转化为接受单个参数的函数,这个转化过程就叫柯里化
//1
def mul(x: Int, y: Int) = x * y
println(mul(10, 10))
//2 闭包
def mulCurry(x: Int) = (y: Int) => x * y
println(mulCurry(10)(9))
//3柯里化
def mulCurry2(x: Int)(y:Int) = x * y
println(mulCurry2(10)(8))
29 抽象控制(没有入参没有出参的Fuction)
//myRunInThread 就是一个抽象控制
//是没有输入, 也没有输出的函数 f1: () => Unit
def myRunInThread(f1: () => Unit) = {
new Thread {
override def run(): Unit = {
f1() //只写了 f1
}
}.start()
}
myRunInThread {
() =>
println("干活咯!5秒完成...")
Thread.sleep(5000)
println("干完咯!")
}
//简写形式
def myRunInThread2(f1: => Unit) = {
new Thread {
override def run(): Unit = {
f1 //只写了 f1
}
}.start()
}
//对于没有输入,也没有返回值函数,可以简写成如下形式
myRunInThread2 {
println("干活咯!5秒完成...~~~")
Thread.sleep(5000)
println("干完咯!~~~")
}
}
30 泛型的支持
1 上界
<? extends A> //java
[T <: A] //或用通配符: [ _ <: A] //scala
2 下界
<? super A>
[T >: A] [ _ >: A]
3 视图界定
<% 的意思是“view bounds”(视界),它比<:适用的范围更广,除了所有的子 类型,还允许隐式转换类型
31 协变、逆变和不变
在Java里,泛型类型都是invariant,比如 List 并不是 List 的子类型。
而scala支持,可以在定义类型时声明(用加号表示为协变,减号表示逆变),
如: trait List[+T] // 在类型定义时声明为协变这样会把List[String]作为List[Any]的子类型。
C[+T]:如果A是B的子类,那么C[A]是C[B]的子类,称为协变
C[-T]:如果A是B的子类,那么C[B]是C[A]的子类,称为逆变
C[T]:无论A和B是什么关系,C[A]和C[B]没有从属关系。称为不变.
32 部分应用函数、偏应用函数
比如定义了一个函数: def sum(a:Int,b:Int,c:Int) = a + b +c,当调用sum的时候,如果不提供所有的参数或某些参数还未知时,比如sum _ ,sum(1,_:Int,3), 这样就生成了所谓的部分应用函数 scala编译器会用Function1, Function2这些类来表示它。
// 定义了一个函数:
def sum(a:Int,b:Int,c:Int) = a + b +c
println(sum(1,2,3));
/*
* 部分应用函数(Partial Applied Function)是缺少部分参数的函数,是一个逻辑上概念偏函数是只对函数定义域的一个子集进行定义的函数。
* 调用sum的时候,如果不提供所有的参数或某些参数还未知时,比如sum _ , sum(1,_:Int,3), 这样就生成了所谓的部分应用函数。
* 部分应用函数只是逻辑上的一个表达,scala编译器会用Function1, Function2这些类来表示它.
*/
val fp_a = sum _;
println(( sum _).apply(1,2,3));
val fp_b = sum(1,_:Int,3)
println(fp_b(2))
println(fp_b(10))
33 至简原则
1 函数至简原则
(1)return可以省略,Scala会使用函数体的最后一行代码作为返回值
(2)如果函数体只有一行代码,可以省略花括号
(3)返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
(4)如果有return,则不能省略返回值类型,必须指定
(5)如果函数明确声明unit,那么即使函数体中使用return关键字也不起作用
(6)Scala如果期望是无返回值类型,可以省略等号
(7)如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
(8)如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
(9)如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略
2 lambda表达式调用匿名函数 (参数名:参数类型,…) => {函数体}
f1((x:String) => {println(x)})
简化1:参数类型可省略,根据形参自动推导
f1((x) => {println(x)})
1
简化2:省略参数类型,如果参数只有一个,参数小括号可以省略
f1(x => {println(x)})
1
简化3:如果函数体只有一句代码,大括号可以省略
f1(x => println(x))
1
简化4:如果参数只在后面的函数体中出现过一次,那么可以用_代替
f1(println(_))
1
简化5:如果println的时候,可以推断出是一个函数,那么_可以省略
f1(println)
val set = Set(1,2,4)
println( set.map(x=>x+"22"))
println( set.map(_+"22"))
参考
1 https://my.oschina.net/sunbr/blog/4958457