Martin Odersky
scala最适合用在算法描述领域,java适合用在指令编程领域。
学习Scala从了解编程范式开始
- 命令式编程(命令式编程关心解决问题的步骤)
- 这就是命令式编程——你要做什么事情,你得把达到目的的步骤详细的描述出来,然后交给机器去运行
- 这也正是命令式编程的理论模型——图灵机的特点
- 面向对象编程
- 函数式编程(函数式编程关心数据的映射)
- 函数式里面的函数有三个重要的特征,而这三个特征就是函数的全部:
- 函数有一个参数和一个返回值
- 无论什么情况下同一个函数对确定的参数始终有同样的的返回值
- 函数的参数和返回值可以是函数
- 逻辑式编程
为什么Spark会选择Scala?
- API能做得优雅; 这是框架设计师第一个要考虑的问题,框架的用户是应用开发程序员,API是否优雅直接影响用户体验。
- 能融合到Hadoop生态圈,要用JVM语言; Hadoop现在是大数据事实标准,Spark并不是要取代Hadoop,而是要完善Hadoop生态。JVM语言大部分可能会想到Java,但Java做出来的API太丑,或者想实现一个优雅的API太费劲。
- 速度要快; Scala是静态编译的,所以和JRuby,Groovy比起来速度会快很多,非常接近Java。
- scala为spark做了很多优化工作
语言基础
scala独有的两招:
- 函数式编程(函数是scala语言中的一等公民,函数式编程的一个重要理念就是要尽量使代码由无状态的函数构成,而不是给计算机发出指令)
- 简单的并发编程
- 缺点:Scala (以及任何具有广泛可变性的语言)的代价是,编译器在优化代码方面没有足够的灵活性。Java的提供解决方案是基于运行时来优化代码。
// 在 Scala 中,是没有 static 这个东西的,但是它也为我们提供了单例模式的实现方法,那就是使用关键字 object
object HelloWorld {
def main(args: Array[String]): Unit = {
println("hello world")
val myVal : String = "Foo";
var myVal1 = "Foo";
// https://docs.scala-lang.org/zh-cn/overviews/collections/arrays.html
// Scala数组Array[Int]可看作Java的Int[]
// Array[String]可看作Java的String[]
// Scala数组是一种泛型。即可以定义一个Array[T],T可以是一种类型参数或抽象类型
val a1 = Array(1, 2, 3)
val a2 = a1 map (_ * 3)
val a3 = Array("Hi", "I", "heard", "about", "Spark")
}
}
Scala函数
// 方法一
def 方法名(参数: 参数类型): 返回值类型 = {
code block
}
// 方法二
// 不写明返回值的类型,程序会自行判断
def 方法名(参数: 参数类型) = {
code block
}
// 方法三
def 方法名(参数: 参数类型): 返回值类型 = x + y
// 方法四
val 方法名 = (参数: 参数类型) => func_code
val addInt = (x: Int,y: Int) => x + y
// 方法四引申高级用法,将函数作为参数
val hello = (name: String) => println(s"hello $name")
函数与方法
容器
scala和java容器类型转换
https://docs.scala-lang.org/zh-cn/overviews/collections/conversions-between-java-and-scala-collections.html
Scala的容器库特别强调不可变性,因此提供了大量的新方法将一个容器变换成一个新的容器。
例如,你可能想要像访问Scala容器一样访问某个Java容器,或者你可能想将一个Scala容器像Java容器一样传递给某个Java方法。
import collection.JavaConverters._
Scala中的下划线
https://blog.csdn.net/i6448038/article/details/50017427
object Sample {
def main (args: Array[String]) {
// (最典型,最常用)这里的下划线代表了集合中的“某(this)”一个元素。这个用法很常见,在foreach等语句中也可以使用
val newArry= (1 to 10).map(_*2)
println(newArry)
// 元组Tuple
val value=(1,2)
print(value._1)
}
}
Scala类相关
object(没有静态方法和静态字段)
class
case class
样例类是Scala 用于对象模式匹配,并且它不需要大量的样板代码。
样例类是带case 修饰符的类。通过这个修饰符,Scala 编译器对我们的类添加了一些语法上的便利。
case class C(name:String,age:Int) // 样例类: name,age 都会被视为字段
val c = C("zhang san",20) // 不需要 new
println("name:" + c.name + " ;age: " + c.age) // name,age 都会被视为字段
println(c.toString) // 自动实现了 toString
println(c.hashCode) // 自动实现了 hashCode
val c2 = c.copy(name="li si")
println(c2.toString)
println(c.equals(c2)) // 自动实现了 equals
- Scala 编译器会为样例类添加一个和类同名的工厂方法。这意味着可以使用 C(…) 来构造对象,而不是使用 new C(…) 来构造对象。这样使得代码中不再到处是new 关键字。
- Scala 编译器会为样例类参数列表中的参数都隐式获取一个val 前缀,因此它们都会被当作字段处理
- Scala 编译器会帮我们以“自然”的方式实现toString,hashCode,equals 方法。这些方法会分别打印、哈希、比较包含类及所有入参的整棵树。
- 因为Scala 的 == 总是代理给equals 方法,因此样例类总是以结构化的方式作比较。
- Scala 编译器还会添加一个copy 方法用于制作修改过的拷贝。这个方法支持带名字的参数,以及缺省参数:可以通过带名字的参数给出希望做的修改;然后对于任何未给出的参数,都会使用旧对象中的原值。
trait 特点 特性
(在Scala中可以通过特征(trait)实现多重继承)
类与单例对象
函数式编程范式
map
filtermap
flatmap
foreach
reduce
sum
foreach只是用在不需要对集合执行映射操作,但需要遍历集合时才用到。总而言之,foreach用于遍历集合,而map用于映射(转换)集合到另一个集合
DataStream.flatmap(_.split(" ")).map((_, 1)).keyBy(0).sum(1)
创建匿名函数
=>右边是函数体,左边表示函数的参数类型
data.filter(_.nonEmpty)
data.filter(x => x!=null)