为什么要使用 Scala 语言?Scala 语言的优势在哪里?

Scala是一门多范式的编程语言,一种类似Java的编程语言,设计初衷是实现可伸缩的语言、并集成面向对象编程和函数式编程的各种特性。
随着Java的版本不断升级,Java的特性是越来越靠近Scala,Java8引入函数式编程,Java11才有了类型推断,但是仍然没有Scala那么简洁和强大:

Scala的生态完善,就应用来说,有大数据流计算框架spark、分布式发布订阅系统Kafka、Akka、Play和Lift等Web框架、Rpc框架Finagle、测试框架Specs等等。
编程语言很多,Go、Python、Scala、Kotlin、Rust、Ruby、Dart、Julia,讨论比较多的是Scala、Go、Python,就目前的应用而言:
Go适合服务端、桌面应用程序开发。
Scala适合服务端、大数据、数据挖掘、NLP、图像识别、机器学习、深度学习…等等开发。
Python适合做网络爬虫、自动化运维、快速地实现算法的原型。
但是Python仍有一些不足之处。
Python性能是个问题,而且多线程并发是劣势。
Python大型项目,架构和重构是灾难。
Python的代码缩进是个坑,当你在使用Python,一小部分代码的修改可能导致你要重新调整整个文件的缩进。

https://www.runoob.com/scala/scala-data-types.html

scala入门:
scala数据类型除常用外的类型:

Unit 表示无值,和其他语言中void等同。用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()。
Null null 或空引用
Nothing Nothing类型在Scala的类层级的最底端;它是任何其他类型的子类型。
Any Any是所有其他类的超类
AnyRef AnyRef类是Scala里所有引用类(reference class)的基类

在 Scala 中,使用关键词 “var” 声明变量,使用关键词 “val” 声明常量。

Scala 中使用 val 语句可以定义函数,def 语句定义方法。
注意有些翻译上函数(function)与方法(method)是没有区别的。

方法声明

def functionName ([参数列表]) : [return type]
如果你不写等于号和方法主体,那么方法会被隐式声明为抽象(abstract),包含它的类型于是也是一个抽象类型。

Scala 方法定义格式如下:
def functionName ([参数列表]) : [return type] = {
function body
return [expr]
}

  1. object add{
  2. def addInt( a:Int, b:Int ) : Int = {
  3. var sum:Int = 0
  4. sum = a + b
  5. return sum
  6. }
  7. }

如果方法没有返回值,可以返回为 Unit,这个类似于 Java 的 void, 实例如下:

  1. object Hello{
  2. def printMe( ) : Unit = {
  3. println("Hello, Scala!")
  4. }
  5. }

方法调用

以下是调用方法的标准格式:
functionName( 参数列表 )
如果方法使用了实例的对象来调用,我们可以使用类似java的格式 (使用 . 号):
[instance.]functionName( 参数列表 )

  1. object Test {
  2. def main(args: Array[String]) {
  3. println( "Returned Value : " + addInt(5,7) );
  4. }
  5. def addInt( a:Int, b:Int ) : Int = {
  6. var sum:Int = 0
  7. sum = a + b
  8. return sum
  9. }
  10. }

Scala 函数传名调用(call-by-name)

Scala的解释器在解析函数参数(function arguments)时有两种方式:

  • 传值调用(call-by-value):先计算参数表达式的值,再应用到函数内部;
  • 传名调用(call-by-name):将未计算的参数表达式直接应用到函数内部

在进入函数内部前,传值调用方式就已经将参数表达式的值计算完毕,
传名调用是在函数内部进行参数表达式的值计算的。
这就造成了一种现象,每次使用传名调用时,解释器都会计算一次表达式的值。

  1. object Test {
  2. def main(args: Array[String]) {
  3. delayed(time());
  4. }
  5. def time() = {
  6. println("获取时间,单位为纳秒")
  7. System.nanoTime
  8. }
  9. def delayed( t: => Long ) = {
  10. println("在 delayed 方法内")
  11. println("参数: " + t)
  12. t
  13. }
  14. }

以上实例中我们声明了 delayed 方法, 该方法在变量名和变量类型使用 => 符号来设置传名调用。执行以上代码,输出结果如下:

一般情况下函数调用参数,就按照函数定义时的参数顺序一个个传递。但是我们也可以通过指定函数参数名,并且不需要按照顺序向函数传递参数,实例如下:

  1. object Test {
  2. def main(args: Array[String]) {
  3. printInt(b=5, a=7);
  4. }
  5. def printInt( a:Int, b:Int ) = {
  6. println("Value of a : " + a );
  7. println("Value of b : " + b );
  8. }
  9. }

Scala 允许你指明函数的最后一个参数可以是重复的,即我们不需要指定函数参数的个数,可以向函数传入可变长度参数列表。
Scala 通过在参数的类型之后放一个星号来设置可变参数(可重复的参数)。例如:

  1. object Test {
  2. def main(args: Array[String]) {
  3. printStrings("Runoob", "Scala", "Python");
  4. }
  5. def printStrings( args:String* ) = {
  6. var i : Int = 0;
  7. for( arg <- args ){
  8. println("Arg value[" + i + "] = " + arg );
  9. i = i + 1;
  10. }
  11. }
  12. }

递归函数在函数式编程的语言中起着重要的作用。Scala 同样支持递归函数。递归函数意味着函数可以调用它本身。以上实例使用递归函数来计算阶乘:

  1. object Test {
  2. def main(args: Array[String]) {
  3. for (i <- 1 to 10)
  4. println(i + " 的阶乘为: = " + factorial(i) )
  5. }
  6. def factorial(n: BigInt): BigInt = {
  7. if (n <= 1)
  8. 1
  9. else
  10. n * factorial(n - 1)
  11. }
  12. }

默认参数值

  1. object Test {
  2. def main(args: Array[String]) {
  3. println( "返回值 : " + addInt() );
  4. }
  5. def addInt( a:Int=5, b:Int=7 ) : Int = {
  6. var sum:Int = 0
  7. sum = a + b
  8. return sum
  9. }
  10. }

高阶函数(Higher-Order Function)就是操作其他函数的函数。
Scala 中允许使用高阶函数, 高阶函数可以使用其他函数作为参数,或者使用函数作为输出结果。
以下实例中,apply() 函数使用了另外一个函数 f 和 值 v 作为参数,而函数 f 又调用了参数 v:

  1. object Test {
  2. def main(args: Array[String]) {
  3. println( apply( layout, 10) )
  4. }
  5. // 函数 f 和 值 v 作为参数,而函数 f 又调用了参数 v
  6. def apply(f: Int => String, v: Int) = f(v)
  7. def layout[A](x: A) = "[" + x.toString() + "]"
  8. }

我们可以在 Scala 函数内定义函数,定义在函数内的函数称之为局部函数

  1. object Test {
  2. def main(args: Array[String]) {
  3. println( factorial(0) )
  4. println( factorial(1) )
  5. println( factorial(2) )
  6. println( factorial(3) )
  7. }
  8. def factorial(i: Int): Int = {
  9. def fact(i: Int, accumulator: Int): Int = {
  10. if (i <= 1)
  11. accumulator
  12. else
  13. fact(i - 1, i * accumulator)
  14. }
  15. fact(i, 1)
  16. }
  17. }

Scala 中定义匿名函数的语法很简单,箭头左边是参数列表,右边是函数体
使用匿名函数后,我们的代码变得更简洁了。下面的表达式就定义了一个接受一个Int类型输入参数的匿名函数:
var inc = (x:Int) => x+1
以上实例的 inc 现在可作为一个函数,使用方式如下:
var x = inc(7)-1
上述定义的匿名函数,其实是下面这种写法的简写:
def add2 = new Function1[Int,Int]{
def apply(x:Int):Int = x+1;
}

  1. object Demo {
  2. def main(args: Array[String]) {
  3. println( "multiplier(1) value = " + multiplier(1) )
  4. println( "multiplier(2) value = " + multiplier(2) )
  5. }
  6. var factor = 3
  7. val multiplier = (i:Int) => i * factor
  8. }

Scala 闭包

闭包是一个函数,返回值依赖于声明在函数外部的一个或多个变量。
闭包通常来讲可以简单的认为是可以访问一个函数里面局部变量的另外一个函数
如下面这段匿名的函数:
val multiplier = (i:Int) => i 10
函数体内有一个变量 i,它作为函数的一个参数。如下面的另一段代码:
val multiplier = (i:Int) => i
factor
在 multiplier 中有两个变量:i 和 factor。其中的一个 i 是函数的形式参数,在 multiplier 函数被调用时,i 被赋予一个新的值。然而,factor不是形式参数,而是自由变量,考虑下面代码:
var factor = 3
val multiplier = (i:Int) => i factor
这里我们引入一个自由变量 factor,这个变量定义在函数外面。
这样定义的函数变量 multiplier 成为一个”闭包”,因为它*引用到函数外面定义的变量,定义这个函数的过程是将这个自由变量捕获而构成一个封闭的函数

  1. object Test {
  2. def main(args: Array[String]) {
  3. println( "muliplier(1) value = " + multiplier(1) )
  4. println( "muliplier(2) value = " + multiplier(2) )
  5. }
  6. var factor = 3
  7. val multiplier = (i:Int) => i * factor
  8. }

声明数组

  1. var z:Array[String] = new Array[String](3)
  2. var z = new Array[String](3)

以上语法中,z 声明一个字符串类型的数组,数组长度为 3 ,可存储 3 个元素。我们可以为每个元素设置值,并通过索引来访问每个元素,如下所示:
z(0) = “Runoob”; z(1) = “Baidu”; z(4/2) = “Google”
最后一个元素的索引使用了表达式 4/2 作为索引,类似于 z(2) = “Google”
我们也可以使用以下方式来定义一个数组:
var z = Array(“Runoob”, “Baidu”, “Google”)

处理数组

数组的元素类型和数组的大小都是确定的,所以当处理数组元素时候,我们通常使用基本的 for 循环。
以下实例演示了数组的创建,初始化等处理过程:

  1. bject Test {
  2. def main(args: Array[String]) {
  3. var myList = Array(1.9, 2.9, 3.4, 3.5)
  4. // 输出所有数组元素
  5. for ( x <- myList ) {
  6. println( x )
  7. }
  8. // 计算数组所有元素的总和
  9. var total = 0.0;
  10. for ( i <- 0 to (myList.length - 1)) {
  11. total += myList(i);
  12. }
  13. println("总和为 " + total);
  14. // 查找数组中的最大元素
  15. var max = myList(0);
  16. for ( i <- 1 to (myList.length - 1) ) {
  17. if (myList(i) > max) max = myList(i);
  18. }
  19. println("最大值为 " + max);
  20. }
  21. }

多维数组

多维数组一个数组中的值可以是另一个数组,另一个数组的值也可以是一个数组。矩阵与表格是我们常见的二维数组。
以上是一个定义了二维数组的实例:
val myMatrix = Array.ofDimInt
实例中数组中包含三个数组元素,每个数组元素又含有三个值。
接下来我们来看一个二维数组处理的完整实例:

  1. import Array._
  2. object Test {
  3. def main(args: Array[String]) {
  4. val myMatrix = Array.ofDim[Int](3, 3)
  5. // 创建矩阵
  6. for (i <- 0 to 2) {
  7. for ( j <- 0 to 2) {
  8. myMatrix(i)(j) = j;
  9. }
  10. }
  11. // 打印二维阵列
  12. for (i <- 0 to 2) {
  13. for ( j <- 0 to 2) {
  14. print(" " + myMatrix(i)(j));
  15. }
  16. println();
  17. }
  18. }
  19. }

合并数组

  1. import Array._
  2. object Test {
  3. def main(args: Array[String]) {
  4. var myList1 = Array(1.9, 2.9, 3.4, 3.5)
  5. var myList2 = Array(8.9, 7.9, 0.4, 1.5)
  6. var myList3 = concat( myList1, myList2)
  7. // 输出所有数组元素
  8. for ( x <- myList3 ) {
  9. println( x )
  10. }
  11. }
  12. }

Scala List(列表)

Scala 列表类似于数组,它们所有元素的类型都相同,但是它们也有所不同:
列表是不可变的,值一旦被定义了就不能改变,其次列表 具有递归的结构(也就是链接表结构)而数组不是。。
列表的元素类型 T 可以写成 List[T]。例如,以下列出了多种类型的列表:

  1. // 字符串列表
  2. val site: List[String] = List("Runoob", "Google", "Baidu")
  3. // 整型列表
  4. val nums: List[Int] = List(1, 2, 3, 4)
  5. // 空列表
  6. val empty: List[Nothing] = List()
  7. // 二维列表
  8. val dim: List[List[Int]] =
  9. List(
  10. List(1, 0, 0),
  11. List(0, 1, 0),
  12. List(0, 0, 1)
  13. )

构造列表的两个基本单位是 Nil::
Nil 也可以表示为一个空列表。
以上实例我们可以写成如下所示:

  1. // 字符串列表
  2. val site = "Runoob" :: ("Google" :: ("Baidu" :: Nil))
  3. // 整型列表
  4. val nums = 1 :: (2 :: (3 :: (4 :: Nil)))
  5. // 空列表
  6. val empty = Nil
  7. // 二维列表
  8. val dim = (1 :: (0 :: (0 :: Nil))) ::
  9. (0 :: (1 :: (0 :: Nil))) ::
  10. (0 :: (0 :: (1 :: Nil))) :: Nil

列表基本操作

Scala列表有三个基本操作:

  • head 返回列表第一个元素
  • tail 返回一个列表,包含除了第一元素之外的其他元素
  • isEmpty 在列表为空时返回true

对于Scala列表的任何操作都可以使用这三个基本操作来表达。实例如下:

  1. // 字符串列表
  2. object Test {
  3. def main(args: Array[String]) {
  4. val site = "Runoob" :: ("Google" :: ("Baidu" :: Nil))
  5. val nums = Nil
  6. println( "第一网站是 : " + site.head )
  7. println( "最后一个网站是 : " + site.tail )
  8. println( "查看列表 site 是否为空 : " + site.isEmpty )
  9. println( "查看 nums 是否为空 : " + nums.isEmpty )
  10. }
  11. }

Map(映射)

Map(映射)是一种可迭代的键值对(key/value)结构。
所有的值都可以通过键来获取。
Map 中的键都是唯一的。
Map 也叫哈希表(Hash tables)。
Map 有两种类型,可变与不可变,区别在于可变对象可以修改它,而不可变对象不可以。

  1. // 空哈希表,键为字符串,值为整型
  2. var A:Map[Char,Int] = Map()
  3. // Map 键值对演示
  4. val colors = Map("red" -> "#FF0000", "azure" -> "#F0FFFF")
  5. object Test {
  6. def main(args: Array[String]) {
  7. val colors = Map("red" -> "#FF0000",
  8. "azure" -> "#F0FFFF",
  9. "peru" -> "#CD853F")
  10. val nums: Map[Int, Int] = Map()
  11. println( "colors 中的键为 : " + colors.keys )
  12. println( "colors 中的值为 : " + colors.values )
  13. println( "检测 colors 是否为空 : " + colors.isEmpty )
  14. println( "检测 nums 是否为空 : " + nums.isEmpty )
  15. }
  16. }

输出 Map 的 keys 和 values

  1. object Test {
  2. def main(args: Array[String]) {
  3. val sites = Map("runoob" -> "http://www.runoob.com",
  4. "baidu" -> "http://www.baidu.com",
  5. "taobao" -> "http://www.taobao.com")
  6. sites.keys.foreach{ i =>
  7. print( "Key = " + i )
  8. println(" Value = " + sites(i) )}
  9. }
  10. }

类和对象

类是对象的抽象,而对象是类的具体实例。类是抽象的,不占用内存,而对象是具体的,占用存储空间。类是用于创建对象的蓝图,它是一个定义包括在特定类型的对象中的方法和变量的软件模板。
我们可以使用 new 关键字来创建类的对象,实例如下:

  1. import java.io._
  2. class Point(xc: Int, yc: Int) {
  3. var x: Int = xc
  4. var y: Int = yc
  5. def move(dx: Int, dy: Int) {
  6. x = x + dx
  7. y = y + dy
  8. println ("x 的坐标点: " + x);
  9. println ("y 的坐标点: " + y);
  10. }
  11. }
  12. object Test {
  13. def main(args: Array[String]) {
  14. val pt = new Point(10, 20);
  15. // 移到一个新的位置
  16. pt.move(10, 10);
  17. }
  18. }

继承

  1. class Point(xc: Int, yc: Int) {
  2. var x: Int = xc
  3. var y: Int = yc
  4. def move(dx: Int, dy: Int) {
  5. x = x + dx
  6. y = y + dy
  7. println ("x 的坐标点: " + x);
  8. println ("y 的坐标点: " + y);
  9. }
  10. }
  11. class Location(override val xc: Int, override val yc: Int, val zc :Int) extends Point(xc, yc){
  12. var z: Int = zc
  13. def move(dx: Int, dy: Int, dz: Int) {
  14. x = x + dx
  15. y = y + dy
  16. z = z + dz
  17. println ("x 的坐标点 : " + x);
  18. println ("y 的坐标点 : " + y);
  19. println ("z 的坐标点 : " + z);
  20. }
  21. }

Trait(特征)

  1. trait Equal {
  2. def isEqual(x: Any): Boolean
  3. def isNotEqual(x: Any): Boolean = !isEqual(x)
  4. }

以上Trait(特征)由两个方法组成:isEqualisNotEqual。isEqual 方法没有定义方法的实现,isNotEqual定义了方法的实现。子类继承特征可以实现未被实现的方法。所以其实 Scala Trait(特征)更像 Java 的抽象类。
以下演示了特征的完整实例:

  1. /* 文件名:Test.scala
  2. * author:菜鸟教程
  3. * url:www.runoob.com
  4. */
  5. trait Equal {
  6. def isEqual(x: Any): Boolean
  7. def isNotEqual(x: Any): Boolean = !isEqual(x)
  8. }
  9. class Point(xc: Int, yc: Int) extends Equal {
  10. var x: Int = xc
  11. var y: Int = yc
  12. def isEqual(obj: Any) =
  13. obj.isInstanceOf[Point] &&
  14. obj.asInstanceOf[Point].x == x
  15. }
  16. object Test {
  17. def main(args: Array[String]) {
  18. val p1 = new Point(2, 3)
  19. val p2 = new Point(2, 4)
  20. val p3 = new Point(3, 3)
  21. println(p1.isNotEqual(p2))
  22. println(p1.isNotEqual(p3))
  23. println(p1.isNotEqual(2))
  24. }
  25. }
  26. $ scalac Test.scala
  27. $ scala Test
  28. false
  29. true
  30. true

模式匹配

  1. object Test {
  2. def main(args: Array[String]) {
  3. println(matchTest("two"))
  4. println(matchTest("test"))
  5. println(matchTest(1))
  6. println(matchTest(6))
  7. }
  8. def matchTest(x: Any): Any = x match {
  9. case 1 => "one"
  10. case "two" => 2
  11. case y: Int => "scala.Int"
  12. case _ => "many"
  13. }
  14. }