模糊概念列表
先记录,后续学完再重点攻破
- 方法和函数
- 协变和逆变
- lambda到底是啥
- trait和interface的区别
- currying(柯里化)
- 提取器对象
- 隐式转换和隐式参数
- case定义的类与普通类的区别
- 传名参数和传值参数
- 偏函数
方法和函数
参考文章:Scala中方法和函数的区别
- 定义方式不一样:
def
定义的叫方法,函数用val/var
定义
def m(x: Int) = 2*x
val f = (x: Int) => x*2
- 无参形式不一样:方法可以没有参数列表,但函数必须有
//方法
def a = 23
//函数
val b= => 23
- 调用方式不一样:方法名代表方法调用,函数名只代表函数本身
scala> def m = 23
m: Int
//可以在命令行中将m作为最终表达式执行,但实际执行的是函数调用
scala> m
res7: Int = 23
scala> def m1(x: Int) = x*2
m1: (x: Int)Int
//不可以将m1作为最终表达式执行,因为有参数
scala> m1
<console>:13: error: missing argument list for method m1
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `m1 _` or `m1(_)` instead of `m1`.
m1
^
//执行的时候加括号和参数
scala> m1(3)
res11: Int = 6
scala> def f = ()=>24
f: () => Int
//无参的形式,也必须有参数列表(),可以使用f作为最终表达式执行,但执行结果是一个该函数的结构描述
scala> f
res10: () => Int = $$Lambda$1107/814300680@2e86807a
//执行必须加括号
scala> f()
res12: Int = 24
scala> def f1 = (x: Int) => 2 * x
f1: Int => Int
//有参的函数,也可以使用函数名(f1)作为最终表达式
scala> f1
res13: Int => Int = $$Lambda$1108/1842571958@50a13c2f
//执行的时候,加括号和参数
scala> f1(2)
res15: Int = 4
- 方法自动转换为函数(ETA expansion):困扰我们的地方是有的需要传函数的地方,也可以传方法,所以我们才会疑惑
- 方法可以强制转换为函数: 方法后面加
_
,记得中间有个空格哦。
scala> def m4(x: Int) = 3*x
m4: (x: Int)Int
scala> val f5 = m4 _
f5: Int => Int = $$Lambda$1138/1175368310@4ffe3d42
- 传名参数:无参的的方法
协变和逆变
+A:协变
-A:逆变
简单来说,假如Person[+A],那么如果你定义了2个Person
val person1 = new Person[A1]
val person2 = new Person[A2]
如果A1是A2的子类型,那么person1也是person的子类型,协变正相反。
函数的参数是逆变的,函数的返回是协变的。
lambda
一个无名函数,带=>
trait和interface
先来说说Java
中的interface
特征,我们可以在接口中定义如下信息:
- 关键字:
interface
- 属性定义:但必须是
final static
标识的,也就是不可变,类属性,默认不需要添加final static
,自动添加 - 被继承标识:其他类继承接口需要用
implements
关键字 方法定义:接口中只能有抽象方法,默认是
abstract
关键字修饰继承其他接口:用
extends
继承,多个用逗号隔开。
接下来我们再来对比下trait
看看:
- 关键字:
trait
。 - 属性定义:必须初始化,无所谓用
val
或var
。 - 方法定义:抽象方法和有实现的方法也可以。
- 被继承:
extends
,多个情况下用with
关键字链接。 - 继承多个特质:
extends
,多个用with
链接。
整体来说,trait
比interface
强大点,可以定义实现的方法,也可以定义可变的属性
curry
柯里化,一个test(a:Int,b:Int)
变成了test(a:Int)(b:Int)
就是curry
,test(2)
返回的是一个函数。
提取器对象
含有unapply
方法的对象,unapply
与apply
的操作相反,apply
将传入的参数组装成对象,unapply
将对象反解析成传入的参数。
case定义的类
会自动生成如下方法:
- apply
- unapply
- getter
- toString
- hashCode
- equals
- copy
偏函数
map
中用case
来定义的匿名函数。
隐式转换和隐式参数
隐式转换的作用,是当某个类缺少某个方法,你无法直接修改源码的情况下,你可以用这个功能去扩展方法。
隐式转换可以理解使用implicit
标识的一个方法,方法参数就一个,表示针对某个对象的隐式转换,方法的类型类似(e:T)=>A
,类似将T隐式转换为A。2.10
后,scala
使用implicit
定义类来达到隐式转换。
隐式参数,指在方法中,某个参数用implicit
定义的,特别是在curry
的方法中,特别有用,你可以不用给这个implicit
定义的参数赋值,他会去找默认定义的值填上,当然你也可以显式赋值。如果参数类型是匿名函数,那这个隐式参数也是隐式转换。
call-by name/call-by value
传值调用(()=>Unit),先计算值,再调用,调用必须加()。传名调用(=>Unit),只在第一次调用的时候才计算,调用只需要用参数名就行。