Scala语言是很注重一致性(consistency)的,Scala的简洁性(concision)都是由其一致性带来的。
Scala的看上去很复杂,但是它在概念上是非常一致的。弄清了几个概念后,也就不觉得复杂了,反倒是比Java的简单。
1. OO + FP
1.1 一切都是对象
更精确地说,应该是“一切值都是对象”。
整数, 浮点数等基本类型(primitive type)是对象
123.toByte 3.14.toInt
在Java中,primitive type不是对象,打破了一致性。
函数是对象
val compare = (x: Int, y: Int) => x > y compare(1, 2)
不再有静态方法(static method)和静态属性(static field)。Java中的静态方法(static method)和静态属性(static field),有点打破了面向对象,因为它们不属于一个实例,而是属于类。在Scala中,静态方法和静态属性也属于对象,具体来说,属于Scala中的单例object。这样,静态成员和普通成员统一了起来,都附属于某个实例(instance)。
object Dog { val sound = "wang wang" //static field }
1.2 函数是值
函数是一等公民,跟普通的值没区别
可以当作参数传递
val compare = (x: Int , y: Int ) => x > y list sortWith compare
不管它是实例的方法
class AComparator { def compare(x: Int , y: Int ) = x > y } list sortWith ( new AComparator ).compare
还是匿名子句
object annonymous extends scala.Function2[Int , Int , Boolean] { override def apply(x: Int , y: Int ) = x > y } list sortWith annonymous
1.3 一切操作都是函数调用
运算符是函数调用
1 + 1 1.+(1) 1.>(0) 1 > 0 (1 > 0).&(2 > 1) (1 > 0) & 2 > 1 stack.push(10) stack push 10 stack.pop stack pop
注意,上述代码中,只有一个参数或零个参数的方法在调用时可以省略”.” 和”()”。
更多的符号需要用作方法名
def !@#%^&*\-<=>?|~:/ = println("noop" ) def √(x: Double ) = Math.sqrt( x) val Π = Math.Pi val r = √( 9*Π)
‘<’, ‘>’ 更适合作方法名,所以用’[’ 和‘]’ 来表示类型参数
for语句是函数调用
for (i <- List(1, 2)) { println(i) } List(1, 2) foreach { i => println(i)} for (i <- List(1, 2)) yield { i + 10 } List(1, 2) map {i => i + 10}
更多的例子
// synchronized is function call instead of keyword def check = synchronized { // isInstanceOf is function call instead of keyword 100.isInstanceOf[ String ] }
额外的好处:自左向右顺序书写语句
stack.pop.asInstanceOf[ Int ] // (Integer) stack.pop() in Java
1.4 一切操作都有返回值
默认返回最后一条语句的值,也可以用return 显式返回
val r1 = { // return 3 val a = 1 val b = 2 a + b } val r2 = if (true) 1 else 2 val r3 = // return (): Unit for (i <- List(1, 2)) { println(i) } val r4 = // return List(11, 12) for (i <- List(1, 2)) yield { i + 10 } val r5 = // return java.io.File try { val f = new File("afile") f } catch { case ex: IOException => null }
Scala里不再像C/C++, Java,区分语句(statement)和表达式(expression)。Scala里没有statement,只有expression,因此一切操作都是表达式,都有返回值。Scala可以说是expression-oriented。
关于语句和表达式的区别,可以看”Scala in depth”一书中的比较:
Statement Versus Expression A statement is something that execute; an expression is something that evaluates to a value.
1.5 总结:一切都是对象+数据即操作,操作即数据 = OO + FP
2. 一致的类型系统(type system)
2.1 scala class hierarchy
2.2 根类 Any
Scala 上有一个共同的根类Any。Any统一了基本类型和引用类型。
2.3 两个尾类Null和Nothing。
JVM上的null是Null类,它是所有AnyRef的子类。
Nothing是所有类型(包括AnyVal和AnyRef)的子类。它没有值(实例),用于处理一些特殊情况,例如出错时返回该类型。
2.4 Invariant, Covariant, Contravariant
2.5 类型参数(type parameter) VS. 抽象类型成员(abstract type member)
trait Pair[K, V] {
def get(key: K): V
def set(key: K, value: V)
}
class PairImpl extends Pair[Dog, Fox] {
def get(key: Dog): Fox
def put(key: Dog, value: Fox)
}
类型参数的名字会发散到所有子类和引用的类,因此改变类型名字时需要修改很多地方
trait Pair{
type K // deferred type
type V // deferred type
defget(key: K): V
defset(key: K, value: V)
}
class PairImpl extends Pair{
type K= Dog
type V= Fox
defget(key: K): V
defset(key: K, value: V)
}
抽象类型成员,改变类型名字时,只需要修改一处地方
4. 一些牛人的讨论
这次会上讲PPT时间有点不够,但这个PPT中提到的每句话都是仔细想过的,强调的是Scala中一些概念的一致性,比如,有关Scala的文章中常提到“值都是对象”,准确地说,应该是“值都是对象的实例”,还有,“操作、函数、参数可以互相转化,都是值,都是实例的对象”,这其实是Scala可以扩展的关键,也是OO+FP能够比较好地在Scala中结合的关键。
还有就是Scala的类型体系,看上去复杂,其实是为了修正Java中类型体系的一些问题,并且带了了一个JVM上的完整一致的类型体系。弄清了几个概念后,也就不觉得复杂了,反倒是比Java的简单。
总之,我看到的Scala的简单性都是由其一致性带来的,虽然设计者为了开发人员的习惯作了一点点妥协,但还是非常坚持他的设计理念。
Martin不是一个简单的人,为了在JVM上实现他的设计理念,其后台的具体实现其实是相当复杂和困难的,但他坚持并不断实践,有时候甚至不惜把原来的实现推倒重来,终于在2.6以后有了我们现在看到的相当理想的结果。
#参考资料