Scala 初体验

1,745 阅读4分钟

Spark 框架的开发者团队曾想要寻找一个这样的开发语言:它既能在现有的 JVM 生态上运行,并且还能用优雅的语法表达复杂的数据处理。这个语言就是 Scala。见:为什么 Spark 选择了 Scala ?

image-20200406142032060.png

语言特点

Scala 是一门静态类型的,多范式的编程语言。Scala 的 SDK 运行在 JDK 之上,因此 Scala 可以无缝地融入到成熟的 Java 库内。编写好的 .scala 文件需要先编译成 .class,然后在 JVM 上运行。类似地,Scala 有自己的解释器和编译器:scalascalac

在 Scala 中,一切皆对象,包括一切数据类型;一切皆方法,包括基本的加减乘除;一切语句块皆有返回值。

Scala 具有非常健壮的类型系统,Java 自 JDK 5 起引入的泛型就是由 Scala 语言之父马丁·奥德斯基设计的。也正因如此,Scala 编译器具有强大的 类型推断 能力。

在 Java 中的各种 Lambda 表达式用法在 Scala 中到处可见,原因是 Scala 推崇函数作为一等对象 ( 这是 Scala 的灵魂 )。随着学习的深入,读者可以逐渐感受到 Scala 遵循着一套与 Java 截然不同的 函数式编程 ( Functional Programming,FP ) 风格,同时,Scala 也完全保留了 Java 的 OOP 风格,初学者完全可以选择将 Scala 作为一个 Better Java 来使用。

Scala 具有极强的表现能力和拓展性。表现在:

  1. Scala 追求极致简约的表述。比如它非常热衷于使用 _ 占位符简化表达,有时可以直接用 ( _ + _ ) 来表示两者之和。
  2. 支持运算符重载,前缀表达式,中缀表达式,后缀表达式,具备设计 DSL 语言的能力。
  3. 有复杂的系统来实现宏编程。该特性在 2.x.x 版本中的争议颇多,在 3.0 版本后才逐渐成熟。
  4. 支持隐式表达,隐式拓展。

安装与开始

Scala 在 2021 年发布了 3.0 版本,代号为 Dotty。目前主流的开发用的版本一般为 2.12.x。

windows平台

Windows 平台可以在官网选择 .msi 一键安装,安装程序会根据目录配置 SDK 环境。安装路径不能带空格和中文,比如 C:/program file。或者选择下载 .zip 包然后直接解压到本机路径,手动工配置 SCALA_HOMEPATH,思路和配置 JDK 类似。

Linux平台

Linux 系统下安装 Scala,可以从官网上下载 .tgz 格式的压缩包解压到路径下并配置,然后通过 source /etc/profile 刷新生效。

export SCALA_HOME={your_path}
export PATH= $PATH:$SCALA_HOME/bin

推荐使用 SDKMAN! 工具进行安装。它是一款方便的 JDK 环境管理工具,这个工具还可以一键安装并配置 Java,Scala,Groovy,Maven,Sbt 的环境并进行多版本管理,比如开发者可以随时切换 Scala 版本为 3.1.1,或者是 2.13.0。见:Home - SDKMAN! the Software Development Kit Manager。这款工具对 Linux 环境十分友好,想要在 Windows 平台安装它,可以参考:Windows下安装SDKMAN - 简书 (jianshu.com)

开发工具

Java 开发者可以继续使用 IntelliJ IEDA 无缝过渡到 Scala 开发,这需要在 Settings » Plugins下载导入 Scala 插件。一般情况下只需要在 IDE 内部搜索插件即可安装,或者去 Jet Brains插件官网 将插件下载后再进行本地安装也可以。

scala_plugins.png

Hello World 程序

下面是一个最基本的 Scala 程序:

object HelloScala {
    def main(args:Array[String]): Unit = {
        var word1 : String = "Hello"
        var word2 : String = "Scala"

        //与Java的System.out.println无异。
        println(string1 + "," + string)

        //c语言的格式化输出
        printf("%s,%s",string1,string)

        //Linux/Php 的 $ 引用方式
        printf(s"$string,$string1")
    }
}

主程序声明在 object 关键字修饰的类定义内。Scala 有两种声明类的方式:

  1. class 关键字修饰的普通类。
  2. object 关键字修饰的 单例对象。故名思义,这个类只有一个对象,就是它自身。它的单例模式由 scalac 编译器自动完成,并且单例对象在 静态域 中被构造。正因如此,主函数必须声明在单例对象内。

下面是反编译 Scala object 单例对象字节码后的等价 Java 代码:

public class HelloScala {
 public static void main(String[] args) {HelloScala$.MODULE$.main(args);}
}
class HelloScala${
 static final HelloScala$ MODULE$;
 static {MODULE$ = new HelloScala$();}
 public void main(String[] args){/*main function code*/}
}

可以看到,单例对象的主函数本质上是委托给了另一个类 HelloScala$ 。该类在静态域中生成了唯一的实例 MODULE$,从而实现了单例模式。因此,一个单例对象 HelloScala 在底层会被编译生成两个 .class 文件:HelloScala.classHelloScala$.class

习惯了 Java OOP 风格的程序员通常会在一个 .scala 文件内使用一对同名的 classobject 共同描述一个类型:使用 class 保存类的实例属性,使用 object 保存类的静态属性。此时由 object 修饰的单例对象又被称之为该类的 伴生对象

class Circle{/*defined*/}
object Circle{
    var Pi : Double = 3.1415926
}

def 关键字表示声明一个方法,var 关键字表示声明一个属性,或者是一个变量 ( variable ) 。

声明变量时,名称在前,类型在后。比如:args : Array[String],这里的 [String] 代表 Array 的类型参数 ( 泛型 ) 。同时,Scala 严格区分大小写,比如 Pipi 是两个不同的变量。

Scala 中的 Unit 相当于 Java 的 void。Scala 的一切语句块都有返回值,该 Unit 类型有且仅有一个值:()。同大部分现代的其它编程语言一样,Scala 的每行代码末尾不需要显式地添加 ; 分号。

这段程序还演示了 Scala 三种输出字符串类型的方式,包括 + 运算符的字符串拼接、格式化输出、以及 Scala 独有的 ${} 占位符风格的字符串模板,它通常能避免大段的字符串拼接代码。

关联源码

在以后的开发中可能需要深入到一些库内部研究底层实现,在这里需要将 IDE 开发环境和源码相关联。

首先去官网: All Available Versions | The Scala Programming Language (scala-lang.org) 那里下载对应版本的 scala-2.xx.x.tar.gz,Scala 3.1.1 版本的源码可以关联 Scala 2.13.6。

resoucres.png

习惯上,我们将解压后的 scala-2.xx.x 源码文件夹放到对应 SDK 的 lib 路径下,避免和其它文件弄混。

随后,在 .scala 内随意创建一个 Scala 库的对象 ( 如 scala.concurrent.Future ) ,按住 Ctrl 键,点击该对象 ( 或者对准光标后按下 Ctrl + b ) 进入到它的内部实现。由于目前没有关联源码,因此 IDE 会反馈 Sources not found 并给出 Attach Source 的提示:

un_attach.png

选择上一步骤中解压出的 ~/lib/scala-2.xx.x 文件夹即可完成关联源码。

Scala Doc

Scala Doc 的编写风格和 Java Doc 相同。如:

object ScalaDocTest {
  /**
    * main function. declared in 'object'
    * @param args
    */
  def main(args: Array[String]): Unit = {println("This is a main function.")}

  /**
    * ScalaDocTest.greet()
    * @return return "hello world"
    */
  def greet():String = {return "Hello Scala"}
}

通过终端进入到工程目录下,执行 scaladoc 命令,可以为路径下的指定 ( 或所有的 ) .scala 文件生成 HTML 样式的文档。

scaladoc -d {target_path} {scala_path}

image-20200406135920145.png