目录
Scala简介
Scala既支持面向对象的编程方式,又支持函数式编程。它一方面吸收继承了多种语言中的优秀特性,一方面又没有抛弃Java这个强大的平台,它运行在JVM之上,轻松实现和丰富的Java类库互联互通。Scala 语言的特性有许多,例如高阶函数和对象、抽象类型绑定,actor 使得函数在 Scala 中能是一个子类成为可能,Scala 中的设计模式使得面向对象和函数编程无缝结合。
Scala和java的区别
1、对字符串的支持
Scala采用三个双引号“”“支持换行字符串,Java需要采用“+”进行字符串的连接
2、方法返回值
Scala的return是可选的,方法调用会自动返回最后求值的表达式,如果Scala使用了return则需要显示指定方法的返回值。Java需要显示的采用return进行值的返回。
3、类和方法修饰符的默认值
Scala默认是public,java默认是protected.
4、默认导入的类
Scala默认导入java.lang包、scala包、scala.Predef类。java默认导入java.lang包
5、接口
Scala不支持接口interface,采用trait(类似于Java中的抽象类);java支持接口。
Java的接口与Scala的trait特质区别
【1】接口不会有构造器,特质可以有构造器,并且在实现类继承特质的时候,先要调用特质的构造器。trait构造器的调用顺序小结:
class B{}
trait C extends E{}
trait D extends E{}
A extends B with C with D
(1)先调用父类构造器B
(2)继承多个trait从从左至右依次执行,先调用父trait E构造器,再调用C,再调用D构造器
(3)如果多个trait继承同一个父trait,则父trait只执行一次
(4)所有父类构造器和trait构造器执行完毕之后再执行自己的构造器A
【2】接口中不能有未初始化的属性,且属性的修饰都是public static final,特质中可以有未初始化的属性变量,即抽象字段
【3】java8之前接口中不能有已经实现的方法,之后可以有default方法和静态方法,特质中可以有已实现的方法也有抽象方法
【4】类实现implements多个接口,且用逗号“,”隔开,类继承extends多个trait且用with隔开
【5】java中接口不能继承普通类,但是Scala中特质可以extends class,并且该类成为所有继承trait的类的父类
【6】Scala中为实例提供混入trait(只有某个对象有trait的性质,其他对象没有),java中没有
6、类成员和单例对象
Scala语言机制上支持单例对象和伴生对象,伴生类。伴生类和伴生对象需要在一个类文件中使用,在使用伴生对象时,系统隐式地调用apply生成一个伴生实例的对象。java由类成员,单例对象需要自己实现
7、方法参数
Scala支持以函数作为参数,java不支持。
8、模式匹配
scala 的 pattern matching是类似这样的,
e match {
case Pattern1 => do Something
case Pattern2 if-clause => do others
}
其中,变量 e 后面接一个 match 以及一个代码块,其中每个 case 对应一种可能回匹配的类型,如果匹配成功则执行 => 后面的代码。此外,match case中,只要一个case分支满足并处理了, 就不会继续判断下一个case分支了。(与Java不同,java的switch case需要用break阻止)。
Scala的match case与Java的switch case最大的不同点在于,Java的switch case仅能匹配变量的值,比1、2、3等;而Scala的match case可以匹配各种情况,比如变量的类型、集合的元素、 有值或无值。
———————————————————————————————————
Scala作为一门静态语言,其主要特性:
- Scala是面向对象的
Scala是一个纯面向对象语言,在某种意义上来讲所有数值都是对象。对象的类型和行为是由class和trait来描述的 - Scala是函数式的
Scala是一个函数式语言,在某种意义上来讲所有函数都是数值。Scala为定义匿名函数提供了一种轻量级的语法,它支持高阶(higher-order)函数、允许函数嵌套、支持局部套用(currying)。Scala的case类及其内置支持的模式匹配模型代数类型在许多函数式编程语言中都被使用 - Scala是静态类型的
Scala配备了一套富有表现力的类型系统,该抽象概念以一种安全的和一致的方式被使用 - Scala是可扩展的
Scala的设计承认了实践事实,领域特定应用开发通常需要领域特定语言扩展。Scala提供了一个独特的语言组合机制,这可以更加容易地以类库的形式增加新的语言结构:任何方式可以被用作中缀(infix)或后缀(postfix)操作符闭包按照所期望的类型(目标类型)自动地被构造两者结合使用可方便地定义新语句,无需扩展语法,也无需使用类似宏的元编程工具
Spark选择Scala的原因
- 应用开发的效率很大程度上依靠类库。框架开发者要考虑Spark要融入大Hadoop生态系统需要一个JVM语言,能够开发出优雅的API(而Java api 不优雅,代码量大),静态编译,函数式编程更适合于MapReduce以及大数据模型
- Scala是一门可扩展的语言,类型系统支持,如果说 Java 是一种类型安全的语言,那么毫无疑问,Scala 的类型更加安全,从某种程度上说, Scala 的类型是图灵完备的,而 Java 不是。正是以上这两点大优势, 造成了 Scala 比 Java 更加安全, 同时又具备灵活性, 想象力
- Java 只支持单继承;在 Scala 中,可以进行 mixin
Java 中, 想要一个 singleton ? 要么在 static block 中做,要么利用 Enum 的单例特性完成;在 Scala 中,只要声明为 object,即为单例
Java 中,通过double check延迟加载一个单例;在 Scala 中,只要在 object 中将变量修饰为 lazy 即可
Java 中,想要对集合进行操作需要使用一层一层的 for 循环;在 Scala 中,使用 collection 的一些集合操作,即可类似于SQL进行操作
Java 中, 在并发中想对Future进行回调无法实现,Future 不是 Listenable (无法支持回调);在 Scala 中,本来就主张异步编程,future 和 promise 的配合很好
Java 中,要透明扩展一个第三方库的类需要包装,再加一层;在 Scala 中, 有强大的 implicit 机制做到这一点,同时还能保证类型安全
Scala 的表达力很强,相同功能的代码,用 Java 和 Scala 的行数不可同日而语
Scala:Object与Class的区别
object
在Scala中没有静态方法和静态字段,但object下的成员都是静态的,object不能提供构造器参数,即object必须是无参的。
class
在Scala中,类名可以和对象名为同一个名字,该对象称为该类的伴生对象,类和伴生对象可以相互访问它们的私有属性,但是它们必须在同一个源文件内。声明一个未用priavate修饰的字段,Scala编译器会自动生成一个私有字段和2个公有方法get和set ;若使用了private修饰,则它的方法也将会是私有的。
Scala伴生类和伴生对象
单例对象与类同名时,这个单例对象被称为这个类的伴生对象,而这个类被称为这个单例对象的伴生类。伴生类和伴生对象要在同一个源文件中定义,伴生对象和伴生类可以互相访问其私有成员。不与伴生类同名的单例对象称为孤立对象。
Scala样例类和普通类的区别
(1)普通类在编译后只会生成一个类名.class文件;而样例类会生成伴生类.class文件和伴生对象的.class文件
(2)样例类自动实现了序列化接口
(3)样例类的构造器中的参数如果不被声明为var的话,它默认的是val类型的,但一般不推荐将构造器中的参数声明为var
(4)样例类自动创建伴生对象,同时在里面实现了apply方法,使得在使用的时候可以不直接使用new创建对象。伴生对象中同样会实现unapply方法,从而可以将case class应用于模式匹配匹配某个对象的时候
(5)case class支持模式匹配
(6)默认实现了toString、hashCode、copy、equals方法
(7)case class 构造函数参数是public的,可以直接访问
Scala中的None,Nothing,Null,Nil
None是一个object,是Option的子类型。scala推荐在可能返回空的方法使用Option[X]作为返回类型。获得Option后,可以使用get获得包含的值,或者使用getOrElse获得默认值如果isEmpty为true。
Null是所有AnyRef的子类,在Scala的类型系统中,AnyRef是Any的子类,同时Any子类的还有AnyVal。对应java值类型的所有类型都是AnyVal的子类。所以Null可以赋值给所有的引用类型(AnyRef),不能赋值给值类型,这个java的语义是相同的。 null是Null的唯一对象。
Nothing是所有类型的子类,也是Null的子类。Nothing没有对象,但是可以用来定义类型。
Nil是一个空的List,定义为List[Nothing],根据List的定义List[+A],所有Nil是所有List[T]的子类。
Scala尾递归
递归函数指的是在函数中调用自身的函数。根据递归的调用方式,可以将递归分为首递归和尾递归。在首递归中,函数调用自身之后,再进行其他运算,因此在运行的时候需要不断的使用新的栈帧来保存函数中的临时变量,这种情况下,当递归的层次很多的时候,很容易出现stack overflow的情况。而尾递归则完全不同,在尾递归函数中,所有的计算都在调用之前完成,因此函数可以在调用完成之后释放栈帧,因此不管递归的层次有多深都不会发生stack overflow的情况。
Scala中如何是使用尾递归?以典型的斐波那契数列为例,来看看Scala中如何使用尾递归。
首递归形式的斐波那契数列
def fibonacci(n:Int):Int={
if(n<=2) 1
else fibonacci(n-1)+fibonacci(n-2)
}
当n=5时,其计算过程如下所示:
fibonacci(5)
fibonacci(4) + fibonacci(3)
(fibonacci(3) + fibonacci(2)) + (fibonacci(2) + fibonacci(1))
((fibonacci(2) + fibonacci(1)) + 1) + (1 + 1)
((1 + 1) + 1) + 2
5
尾递归的形式如下所示:
def fibonacciTail(n:Int,acc1:Int,acc2:Int):Int={
if(n<2) acc2
else fibonacciTail(n-1,acc2,acc1+acc2)
}
其调用过程如下所示:
fibonacciTailrec(5,0,1)
fibonacciTailrec(4,1,1)
fibonacciTailrec(3,1,2)
fibonacciTailrec(2,2,3)
fibonacciTailrec(1,3,5)
5
其实在Scala中使用尾递归最关键的地方是需要理解递归函数需要返回的是什么,返回的结果应该如何积聚(本质上就是如何引入合适的累加器来积聚递归程序返回的结果)。Scala对形式上严格的尾递归进行了优化,对于是否是严格尾递归,若不能自行判断, 可使用Scala提供的尾递归标注@scala.annotation.tailrec,这个符号除了可以标识尾递归外,更重要的是编译器会检查该函数是否真的尾递归,若不是,会导致如下编译错误:could not optimize @tailrec annotated method fibonacci: it contains a recursive call not in tail position。
函数式编程好处
- 代码简洁,开发快速
函数式编程大量使用函数,减少了代码的重复,帮助减少业务逻辑和代码的分歧 - 接近自然语言,易于理解
函数式编程的自由度很高,可以写出很接近自然语言的代码 - 更方便的代码管理
函数式编程不依赖、也不会改变外界的状态,只要给定输入参数,返回的结果必定相同。因此,每一个函数都可以被看做独立单元,很有利于进行单元测试(unit testing)和除错(debugging)以及模块化组合 - 易于"并发编程"
函数式编程不需要考虑"死锁"(deadlock),因为它不修改变量,所以根本不存在"锁"线程的问题。不必担心一个线程的数据,被另一个线程修改,所以可以很放心地把工作分摊到多个线程,部署"并发编程"(concurrency)
2103



被折叠的 条评论
为什么被折叠?



