对于 Scala 语言其实很早有所耳闻,但没有真正进一步了解,只知道这门语言在大数据领域很火。正如前几年大数据开发的兴起,也着实让这门基于 JVM 的语言火了一把。因为近期开始参与公司的大数据项目,面对大数据量计算处理需求,基于目前本身 Java 的技术栈远远不够,不得不引入 Spark 之类的大数据框架,而 Spark 是由 Scala 编写的,虽然说 Spark 提供 Java API,但为了能更好了使用 Spark 和及时排查可能遇到的问题,仍是有必要全面学习下 Scala 这门语言。html
本文主要内容涉及以下:java
示例项目:github.com/wrcj12138aa…git
环境支持:程序员
- JDK 8
- SpringBoot 2.1.5
- Maven 3.6.0
- Scala 2.12.8
在认识 Scala 以前,咱们先来弄清楚 Scala 的读法和来历。尤为是做为中国程序员,头疼的就是面对新技术出现的单词,本身却读不出来或者读得很变扭,这里我在百度翻译中查到了 Scala 的音标 [ˈskɑlə]
和单词发音,能够供你们参考。github
接着再简单看下 Scala 的历史:Scala 是由洛桑联邦理工学院的 Martin Odersky 教授主导设计的一门编程语言,面对 Java 严格的语言规范限制,Martin 教授基于 JVM 从新设计了一门更加现代化,可扩展的语言, 而且将这门 Scalable Language 的缩写 Scala 做为命名,这就是 Scala 的来由。从 2003 年末基于 Java 平台的 Scala 发布,到如今稳定版本已经到了 2.12.8,能够说 Scala 的迭代速度仍是很快的web
好了说到正题,要学习 Scala 这门编程语言,首先来解读下官方对 Scala 的描述:spring
Scala combines object-oriented and functional programming in one concise, high-level language. Scala's static types help avoid bugs in complex applications, and its JVM and JavaScript runtimes let you build high-performance systems with easy access to huge ecosystems of libraries.sql
从上面这段话里,咱们能够提取到如下信息:数据库
关于 Scala 语言的丰富特性咱们将在下文的 Scala 语法 一节去学习和感觉;而针对构建高性能程序和框架这一点,目前大数据库领域的项目框架 Spark,Flink,Kafka 等都是基于 Scala 开发的,说明了使用 Scala 对高性能场景下的帮助是毫无疑问的。编程
总体了解 Scala 以后,接下来咱们就开始快速搭建 Scala 环境,而后写咱们的第一行 Scala 代码吧。
搭建 Scala 环境其实很简单,类比配置 Java 语言环境的经历,总结下来就两步骤:
注意:下载 Scala 安装包以前,须要本机环境有 JDK 8 以上 才行。
从Scala 官网下载 2.12.8 版本的语言压缩包,并解压到本地磁盘。
添加 Scala 环境变量,指定为所解压的 Scala 文件夹,因为我本机为 Mac 环境,因此只需在 Shell 配置文件里操做便可,修改 Shell 配置文件 ~/.bash_profile
文件底部添加下面内容, 使用命令source ~/.bash_profile
使配置生效。
完成上述步骤后,命令行输入 scala -version
获得跟下图内容同样时就表示 Scala 语言环境搞定了。
不一样于 Java,Scala 还提供了命令行模式下的交互,专业说法为 REPL(Read-eval-print-loop),详细介绍能够参见Scala REPL OVERVIEW;基于此咱们能够直接在命令行上使用 Scala 语言,若是有过 Python 或者 Node.js 的同窗应该会熟悉这个编程交互方式。
首先在命令行里输入 scala
进去交互模式,而后输入 println("Hello,Scala")
回车,这样一个最简单的 Scala 版 HelloWorld 就是完成了。
这里使用到的 println
就是 Scala 中的打印方法,相似 Java 中的System.out.println
,是否是很简洁呢。
除了使用命令行交互方式以外,咱们再试着实现一个 Scala 类的 HelloWorld。
首先新建一个 HelloWorld.scala
文件, 写一个 main
方法。
看起来,Scala 的写法跟 Java 的 main
方法是否是很不同呢,先试着跑起来,咱们后面再对语法进一步学习。
接下来就须要对源代码进行编译运行了,相似 Java ,Scala 库提供了 Scala 编译器工具 scalac,能够对 Scala 文件编译成字节码;还有解释器工具 scala
用于运行字节码。
使用 scalac 进行编译生成 HelloWorld.class
文件;
scalac HelloWorld.scala
复制代码
最后若要运行,执行 scala HelloWorld
,控制台输入 HelloWorld
就说明运行成功了。
看过最简易的两种 Scala 版 HelloWorld 以后,咱们开始学习一下 Scala 基础语法和特性吧。
Scala 声明变量的关键字有两个:var
& val
。 var
用于可变变量的声明,是 variable
的简写;val
用于常量的声明,是 value
的简写。下面是通常的写法,定义的变量名与类型之间,须要用一个冒号:
隔开。
val x: Int = 1+1
var y: String = "abc"
复制代码
Scala 中容许不使用分号表示语句结束,换行即认为新语句的开始
但与 Java 声明变量时必需要指定变量类型不一样, Scala 具备类型推断的特性,就不须要显示地声明类型,所以咱们能够写成下面形式:
val x = 1+1
var y = "abc"
复制代码
如今再来讲下 var
与 val
的区别:val
用于声明的常量,当赋值以后就不能再改,尝试修改值时会编译错误。
另外,val
变量赋值为一个引用对象时,能够改变对象的属性,但不能再指向其余对象,因此 val
能够理解为 Java 中用 final
修饰的变量。
在 Scala 中一切都是对象,首先看下官方提供的 Scala 类型层次图,有跟 Java 相同的数据类型,也有 Scala 特有的类型。
Java 中有八大基础数据,Scala 则有九个基础数据类型, 除了同样有 Double
,Float
,Long
,Int
,Short
,Byte
,Boolean
,还额外多一个 Unit
类型,它表示不带任何意义的类型,有且仅有一种值:()
,能够理解为 Java 中的 void
,在函数定义时使用表示该函数无返回值。
这些基础类型都有一个父类 AnyVal
,它表示通用的值类型,与 AnyVal
相对的就是引用类型 AnyRef
, Scala 中的集合对象(Set
,Map
),字符串类(String
) ,以及自定义的类则都属于 AnyRef
,至关于 Java 中的 java.lang.Object
。
在 AnyVal
和 AnyRef
之上的就是 Scala 最顶级的类型 ,它定义了一些最通用的方法如 equals
,hashCode
和 toString
。
Scala 类型层次图底部还有两个类型 Nothing
和 Null
,Nothing
是全部类型的子类型,该类型下没有任何一个值,主要用于程序的异常和中断的非正常返回;而 Null
为引用类型的子类型,只有一个值:null
,主要是在与 Java 交互时使用。
不一样的值类型,Scala 也支持类型转换,而且是单向的,下面是值转换的方向图。
而若是尝试非指定方向的类型转换,就不会经过编译,直接提示错误: type mismatch
,以下示例:
在 Scala 中方法和函数一般状况下能够认为是一个东西,只是函数能够做为变量单独使用,能够理解为 Java8 的 Lambda 表达式,首先来看下他们如何定义,这一点跟 Java 仍是有很大不一样的。
上面是函数/方法最简化的定义方式,针对单行的表达式,Scala 是容许省略大括号和 return
关键字的。
首先看下函数的定义,=>
左边为参数列表(这里的类型不能省略),右边的为表达式内容,从上面的 add
函数变量类型输出能够看出 add
函数变量类型为 (Int, Int) => Int
,底层是一个 Lambda 相关的对象。
匿名函数,也叫作闭包:表示没有名字的函数,将定义的函数没有指定变量名称直接使用,如
(x: Int) => x + 1
Scala 定义方法须要使用 def 关键字,这是 Java 所没有的,而且 def
后面紧跟方法名称,参数列表,返回类型和方法体,下面给出常见的方法定义语法
须要注意的是,在 Scala 方法中默认将最后一行的语句结果做为返回值 。(跟 Java 同样,Scala 也有 return
关键字,但为了简洁不多使用)。而若是方法定义返回值类型为 Unit
时,则表示该方法没有返回值。
除此以外,若是调用的函数或者方法没有入参,能够把括号省略。
接下 Scala 函数/方法中所支持的一些特性:可变参数,默认参数,命名参数:
1.可变参数
Scala 容许指明函数的最后一个参数是能够重复的,在参数类型后面放星号 *
,表示可重复的参数,Java 中使用 ...
:
一样函数内部用一个数组接收可变参数。
2.默认参数
Scala 容许为函数参数指定默认值,这样即便调用函数过程当中不传递参数,函数就是采用它的默认参数值,若是传了参数,默认值就会被忽略,这一特性是 Java 所没有的,但使用起来也很方便简单。
3.命名参数
尽管函数定义时指定了参数列表顺序,可是 Scala 支持不按照定义顺序,按照指定参数名字进行参数传递,实例以下:
程序中一般涉及的流程控制主要两种:条件判断和循环处理。
条件判断上,Scala 有与 Java 同样的 if-else
语法,除此以外,Scala 提供了模式匹配的机制进行条件判断。
它是 Java 的 switch
语句的升级版,首先看下语法:
这是使用新关键字 match
, 左侧为被匹配的值,而右侧包含了四个 case
表达式,每一个表示一种条件,最后的 case _
表示匹配其他全部状况,相似 switch-case
语法的 default
做用。
match
表达式容许接受返回值,正如上方式示例会返回 String
类型,因此模式匹配一般定义在函数中,以下:
若是匹配的条件里要执行多个语句,则就须要使用大括号 {}
包含了,须要注意的是模式匹配一旦某个条件匹配成功,就不会执行其余条件的语句,自带了 break
的效果。
Scala 的模式匹配在本文仅进行简单的用法演示,还有丰富的用法详细可见官方文档-模式匹配一节
Scala 循环语法比较特别,使用 for
+ <-
来进行遍历元素,并提供了便捷的 until
方法,to
方法来实现遍历, 须要注意的就是 until
方法采用半闭区间,不包含索引最后一位。而 until
方法,to
方法底层都是构建 Range 对象而后进行遍历。
除了上面简单用法以外,在 for
循环中还能够配合 if
条件进行遍历过滤,以下面简单的示例,是否是很灵活呢。
Scala 中类的概念与 Java 的基本一致,而语法形式和用法上更加丰富灵活。
先看下类的定义,没看错下面是 Scala 最简单的类的定义和使用了,建立了一个 User
对象,赋值给了 user1
变量。
new
关键字用于建立实例,因为当前 User
没有定义任何构造器,所以只有一个默认的无参构造器,能够直接省略括号()
。
再看下一个带有自定义构造器的类如何定义,看起来是否是很奇怪,紧跟类名括号后面的就是构造器所需的参数。
这里会有个疑问:若是有多个构造器函数,那这个类都该如何定义呢?来看下示例写法,基于原有构造器,用 def this(...)
定义了一个新的方法,内部执行 this(name,age)
实际调用了原来的构造器。Scala 将这个定义方法称为辅助构造器,紧跟类名后的为主构造器。
Scala 容许一个类有多个辅助构造器,但只能有一个主构造器,而且辅助构造器内部第一行必须调用主构造器,这里是能够经过多个辅助构造器间接地调用主构造器。
若是辅助构造器有额外的构造参数,则须要添加字段关联,不会像主构造器同样自动生成字段如上面 Person
类的 name
和 age
。
在 Java 开发中,一般咱们会将 Java Bean 的成员变量私有化,而后提供 Getter/Setter 方法供外部操做该变量,那边在 Scala 类中有该如何操做呢,其实也很简单,首先看下示例。成员变量的成员默认是public
的,可使用private
访问修饰符能够在函数外部隐藏它们。
注意这边字段声明时使用了 _
关键字,这个在 Scala 中表示特定类型的默认值,用于占位的做用,若是对于为 String 类型,则该值就是空字符串。
继承是面向对象语言的重要的特性之一,Scala 在继承方面也跟 Java 同样,使用 extends
,而且只容许单继承。继承状况有两种,一种为父类采用了默认构造器,另一种就是父类有自定义的构造器,子类定义时必须知足父类构造器参数的要求,先会执行父类的主构造器,在执行子类本身的构造器。
当子类继承父类后,想要重写父类方法也十分简单,使用 override
关键字修饰须要重写的访问,子类默认执行父类方法例如这样的形式 super.foo()
,咱们只需调整为自定义的实现便可。
Trait 翻译过来就是特征,特性的意思,看似比较新颖,其实它就相似于 Java 的接口,用于扩展咱们的类。跟 Java 的 Interface 同样,Trait 也没法被实例化,一般提供若干个抽象方法和字段,由具体的类使用 extends
关键字实现。
有一点不一样的是,当类须要实现多个 Trait 时,extends
只能指向一个 Trait,其他的 Trait 都须要使用 with
关键字 关联,以下方给出的示例。
Case Class 是 Scala 特有的一种特殊的类,定义方式虽然跟普通类同样,但须要使用 case class
组合关键字修饰。主要用于不可变的数据和模式匹配中。
实例化 Case Class 类时不须要使用 new
关键字,而是有一个默认的apply
方法来负责对象的建立。而且赋值字段都是默认用public val
修饰,让字段不可更改。
额外注意的是当两个 Case Class 对象进行 ==
比较的时候,Scala 是按照值比较,而不是引用比较。
Scala 将单例对象单独用 object
关键字单独声明出来,定义方式用类同样, 如 object Box
。但单例对象也是属于一种特殊的类,有且只有一个实例,而且当它第一次被使用时才会建立。
伴生对象与伴生类
Scala 容许一个单例对象与某个类共用一个名称,而这样二者造成了伴生的关系,单例对象成为该类的伴生对象,而类成为单例对象的伴生类。伴生的关系主要体如今于,伴生对象里定义的成员,能够在伴生类上使用,这一点就是比如在 Java 中 static
成员。
元组也属于 Scala 中一个特殊的类,它容许包含不一样类型的元素,而且为不可变。常见的用法就是当咱们须要函数返回多个值时,这点就可强大了。
首先看下元组的建立和使用
Tuple2 类属于 Scala 定义的元素类,从 Tuple2
...Tuple22
一共有 21 个这样类,每一个类表示当前所对应的参数个数,也就是元组中最多包含 22 个参数。
元组访问起来也很方便,用tuple._n
方式去取第 n 个元素。
除了兼容 Java 的集合框架,Scala 还提供了丰富强大的集合类,咱们主要来学习下 Scala 中最经常使用的几种集合。
一种不包含重复元素的容器对象,分为不可变 Set 和可变 Set 两种,默认为不可变。
不可变 Set 经常使用的操做方法有
xs.contains(x)
,xs(x)
判断集合是否存在该元素xs intersect ys
,xs & ys
两个集合取交集xs union ys
,xs | ys
两个集合取并集xs diff ys
,xs &~ ys
两个集合取差集可变 Set 经常使用的操做方法有
xs add x
添加到集合xs remove x
从集合中移除xs.clear()
清空集合xs(x) = b
,xs.update(x, b)
将集合更新Map 集合特色就是以键值对结构存储。使用语法上有两种:
key -> value
方式添加元素(key, value)
方式添加元素Map 集合与 Set 集合相似,也有不可变和可变之分,默认为不可变。
经常使用的 Map 操做有
map.keys
返回含有全部 key 的可迭代对象map.keySet
返回一个含全部 key 的集合map.values
返回一个含全部 value 的可迭代对象map(k) = v
更新元素map.put(k, v)
新增元素map.remove(k)
移除元素map.clear()
清空 Map 集合Scala 数组与 Java 数组是一一对应的。一样把 Array 做为数组的标识符,下面先看下如何声明一个定长数组和简单使用。
访问数组指定元素时采用括号+索引的形式,当数组元素没有值时,用 null
表示。
Scala 除了定长数组以外,还提供了可变数组的实现 ArrayBuffers
,这个 Scala 提供的可变容器的一种数组实现,能直接访问和修改底层数组,具体使用方式以下:
Scala 提供了许多可变容器集合,如 ArrayBuffer,ListBuffer,链表,队列等等,都封装在
scala.collection.mutable.*
包下。
使用数组就会有越界访问的状况,当 Scala 数组出现越界访问时,抛出 java.lang.IndexOutOfBoundsException
异常。
接触了那么多 Scala 语法和特性以后,咱们经过用 Scala 语言来编写简单的 Spring Boot Web 应用访问 hello 请求,来看下它是如何跟 Java 混合开发的。
咱们使用 IDEA 做为开发工具,要使用 Scala 语言,为了让 IDE 提供更好的 Scala 语言支持,能够先安装 Scala 插件
咱们能够经过 start.spring.io/ 建立一个普通的 SpringBoot 项目,下载到本地后解压用 IDEA 打开。
基于原来的目录接口,新建一个 scala 源代码目录。
Scala 的源代码目录建立后,转到 Pom 文件,修改 XML 配置:
首先引入 Scala 语言库, 这里版本为 2.12.8
加入 Scala 依赖包以后,咱们还须要添加一个用于编译 Scala 的 Maven Plguin。
好了,到此 Scala 的项目配置已经完成,接下来就能够编写 Scala 代码了。
为了接受 hello 请求,项目 POM 先别忘了依赖
spring-boot-starter-web
新建一个 Scala 类 HelloContrroller.scala
,实现一个接受 /hello
请求的方法,以下
而后直接启动项目引导类 com.blog4one.learn.ScalaActionsApplication
,启动日志以下则表示启动成功,默认服务监听 8080 端口。
打开浏览器,输入 http://localhost:8080/hello
,回车便可看到结果。
浏览器成功的响应,也说明了 Scala 编写的 HelloContrroller.scala
能接受并处理响应,而底层仍然是基于 Java 框架 Spring Boot 构建。
好了,基本的 Scala 语法和特性先介绍到这里,经过此次学习想必也看到了 Scala 虽然是基于 JVM 上运行,可是语法和功能都极其灵活,想要掌握还须要花必定精力时间, 后续继续对 Scala 高级特性和用法进行介绍,感兴趣的小伙伴能够关注个人微信公众号,会在第一时间更新哈。