概念
1. Scala是运行在JVM上的语言,是一门彻底面向对象的语言,一切皆对象
- 因为Java和Scala都会被编译为JVM上的字节码,所以Java和Scala可以互相调用。
- Scala中没有基础数据类型,包装类型,所有类型都是类(Class)
2. var定义的值可变,val定义的值不可变
- val 的意义:
- Scala函数式编程中要求值不可变,val符合这特性。
- 在分布式系统中,一般要求值不可变,防止数据传输时值发生改变。如Spark中RDD的值就是不可变的。
类与函数
Array,List,Tuple
相同点:
1.长度都是固定的,不可变长
不同点:
1. Array中的元素值可变,List和Tuple中的元素值不可变
2. Array一般是先确定长度,后赋值,而List和Tuple在声明的时候就需要赋值
3. List和Array的声明不需要new关键字,而Tuple声明不管有无new 都可以
4. Array不可混合类型,List, Tuple可以
Scala的类体系结构
* “=”并不只是用来分割函数签名和函数体的,它的另一个作用是告诉编译器是否对函数的返回值进行类型推断!如果省去=,则认为函数是没有返回值的!
* lazy
创建类时,加了lazy的变量/成员可以不被实例化,而是等到使用时才被实例化。
函数
匿名函数
scala > val fun2_v = (content : String) => {println(content)}
高阶函数
函数的参数是函数,函数的返回值是函数
闭包
For example, closure will keep track of any variable changes outside the function that are being referred to inside the function.
闭包(Closure)是拥有闲置变量(Free variable)的运算式
// 對 doSome 來說,x 是 bound variable
def doSome() = {
val x = 10
// 對 f 來說,x 是 free variable,y 是 bound variable
val f = (y: Int) => x + y
f
}
doSome的例子中,(y: Int) => x + y 形成了一个闭包。
如果你单看: val f = (y: Int) => x + y,看来起x似乎没有定义。实际上,x 是从外部函式捕捉而来。闭包是个捕捉了外部函式变数(或使之继续存活)的函式。
在doSome的例子中,(y: Int) => x + y 形成了一个闭包,因为它将变数 x 关入(close)自己的范围。如果形式闭包的函式物件持续存活,被关闭的变数 x 也会继续存活。就像是延续了变数 x 的生命週期。
注意!閉包關閉的是變數,而不是變數所參考的值。下面這個範例可以證明:
def doOther() = {
var x = 10
val f = x + (_ : Int)
x = 100
f
}
val foo = doOther()
println(foo(20)) // 顯示 120
println(foo(30)) // 顯示 130
关闭的是建立闭包时外部范围下的变数。以上例来说,
第一次呼叫doOther时,建立了x变数,指定值给x变数,而后建立闭包将之关闭。
第二次呼叫doOther时,建立了x变数,指定值给x变数,而后建立闭包将之关闭。
所以foo(20)与foo(20)关闭的根本是不同作用范围的x变数(也就是该次呼叫doOther时所建立的x变数)。
Curry
scala中,一般函数定义以及使用方法如下
def sum(a: Int, b: Int) = a + b
println(sum(1, 2))
而将函数Curry(柯里化),定义和使用方法则变成如下
def sum(a: Int)(b: Int) = a + b
println(sum(1)(2))
所谓柯里化,是指将接受多个参数的函数(例如第一个范例的sum接受两个参数),改为接受单一参数的函数(也就是第二个范例的sum(a: Int)),在函数执行过后传回一个函数物件再套用剩下的参数(也就是第二个范例的(b: Int))。
过程拆分:
def sum(a: Int)(b: Int) = a + b
val s = sum(1)_
println(s(2))
构造函数
主构造函数:Primary Constructor
scala的主构造函数指的是在定义Class时声明的那个函数!简单的例子:
<scala> class MongoClient(val host:String, val port:Int)
对于这个class的定义,实际上它也是同时声明了一个构造函数:this(val host:String, val port:Int), 它就是所谓的主构造函数!
关于构造函数重载
在scala中,构造函数的重载和普通函数的重载是基本一样的,区别只是构造函数使用this关键字指代!当然,也不能指定返回值。
对于重载构造函数:它的第一个语句必须是调用另外一个重载的构造函数或者是主构造函数!当然除了主构造函数以外!这个表述如果再深入地一想,那么我们就可以想到:所有的构造函数在一开始就会首先调用主函数!!这也是为什么:scala对写在Class内的零星的脚本和代码片段的处理是通过移到主构造函数内去执行的原因!
class MongoClient(val host:String, val port:Int) {
def this() = {
val defaultHost = "127.0.0.1"
val defaultPort = 27017
this(defaultHost, defaultPort)
}
}
在上面的代码汇编出错,因为它的第一行并不是在调用另外一个构造函数!
对于Class的field的修饰符有如下约定:
使用var声明field,则该field将同时拥有getter和setter
scala> class MongoClient(var host:String, var port:Int)
使用val声明field,则该field只有getter。这意味着在初始化之后,你将无法再修改它的值。
scala> class MongoClient(val host:String, val port:Int)
如果没有任何修饰符,则该field是完全私有的。
scala> class MongoClient(host:String, port:Int)
如何显示地定义一个字段的getter和setter
在Scala里,如果需要手动地为一个字段添加getter和setter规则是这样的:
字段名应以_在前缀,如_age
getter是一个function,其命名在字段名上去除_即可,如def age=_age
setter的定义看似有些奇怪,其实只是一些约定,熟悉以后就可以了。setter的命名是在去除字段名上去除前缀,然后在后面添加”=”后缀,对是”_=”!然后再接参数列表,再之后就和普通的函数定义没有区别了!
我们再来看这个例子:
class Person(var firstName:String, var lastName:String, private var _age:Int) {
def age = _age //age是一个function,因为没有参数,所以参数列表为空,它是为私有字段_age定义的getter
def age_=(newAge: Int) = _age = newAge //“age_=”是setter的函数名!这里如果写成def age_=(newAge: Int) = {_age = newAge}看上去就不奇怪了。
}
case class
Object对象(单例,相当于static)
基于“纯OO”的理念,scala里没有static关键字的:一切皆对象!
但是为了能实现“全局唯一”的需要,scala支持一种叫做singleton object的对象,也就是限定一个类的实例只有唯一一个的机制。
Trait
Scala Trait(特征) 相当于 Java 的接口,实际上它比接口还功能强大。
与接口不同的是,它还可以定义属性和方法的实现。
一般情况下Scala的类只能够继承单一父类,但是如果是 Trait(特征) 的话就可以继承多个,从结果来看就是实现了多重继承。
Trait(特征) 定义的方式与类类似,但它使用的关键字是 trait,如下所示:
trait Equal {
def isEqual(x: Any): Boolean
def isNotEqual(x: Any): Boolean = !isEqual(x)}
协变 逆变
/ 偏函数
Scala中的Partia Function是一个Trait,它的类型为PartialFunction[A,B],其中接收一个类型为A的参数,返回一个类型为B的结果。
对给定的输入参数类型,偏函数只能接受该类型的某些特定的值。一个定义为(Int) => String 的偏函数可能不能接受所有Int值为输入。这就是“偏”函数
scala> val pf:PartialFunction[Int,String] = {
case 1=>"One"
case 2=>"Two"
case 3=>"Three"
case _=>"Other"
}
pf: PartialFunction[Int,String] = <function1>
scala> pf(1)
res0: String = One
scala> pf(4)
res3: String = Other
当我们定义了pf时,实际上就是创建了一个PartialFunction[Int, String]的子类,其中isDefineAt方法提供类似这样的实现:
def isDefineAt(x: Int):Boolean = x == 1
当我们通过pf(1)去调用该偏函数时,就相当于调用了Int => String函数的apply()方法,从而返回转换后的值“one”。如果传入的参数使得isDifineAt返回false,就会抛出MatchError异常。追本溯源,是因为这里对偏函数值的调用,实则是调用了AbstractPartialFunction的apply()方法(case语句相当于是继承AbstractPartialFunction的子类):
转自:http://www.jianshu.com/p/0a8a15dbb348
http://zhuanlan.51cto.com/art/201703/534053.htm