You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
objectTest {
defmain(args: Array[String]) {
printInt(b=5, a=7);
}
defprintInt( a:Int, b:Int ) = {
println("Value of a : "+ a );
println("Value of b : "+ b );
}
}
简介
Scala 是一门
多范式(multi-paradigm)
的编程语言,设计初衷是要集成面向对象编程和函数式编程的各种特性。Scala 运行在Java虚拟机上,并兼容现有的Java程序。
Scala 源代码被编译成Java字节码,所以它可以运行于JVM之上,并可以调用现有的Java类库。
Scala特性
面向对象特性
函数式编程
静态类型
扩展性
并发性
为什么选择 Scala?
Scala如何工作?
Scala 基础语法
对象 - 对象有属性和行为。
类 - 类是对象的抽象,而对象是类的具体实例。
方法 - 方法描述的基本的行为,一个类可以包含多个方法。
字段 - 每个对象都有它唯一的实例变量集合,即字段。对象的属性通过给字段赋值来创建。
Scala 基本语法注意点:
区分大小写 - Scala是大小写敏感的,这意味着标识Hello 和 hello在Scala中会有不同的含义。
类名 - 对于所有的类名的第一个字母要大写。
如果需要使用几个单词来构成一个类的名称,每个单词的第一个字母要大写。
示例:
class MyFirstScalaClass
方法名称 - 所有的方法名称的第一个字母用小写。
如果若干单词被用于构成方法的名称,则每个单词的第一个字母应大写。
示例:
def myMethodName()
程序文件名 - 程序文件的名称应该与对象名称完全匹配(新版本不需要了,但建议保留这种习惯)。
保存文件时,应该保存它使用的对象名称(记住Scala是区分大小写),并追加".scala"为文件扩展名。 (如果文件名和对象名称不匹配,程序将无法编译)。
示例: 假设"HelloWorld"是对象的名称。那么该文件应保存为'HelloWorld.scala"
**def main(args: Array[String]) **- Scala程序从main()方法开始处理,这是每一个Scala程序的强制程序入口部分。
Scala 注释
Scala 类似 Java 支持单行和多行注释。多行注释可以嵌套,但必须正确嵌套,一个注释开始符号对应一个结束符号。注释在 Scala 编译中会被忽略,实例如下:
空行和空格
一行中只有空格或者带有注释,Scala 会认为其是空行,会忽略它。标记可以被空格或者注释来分割。
换行符
Scala是面向行的语言,语句可以用分号(;)结束或换行符。Scala 程序里,语句末尾的分号通常是可选的。如果你愿意可以输入一个,但若一行里仅 有一个语句也可不写。另一方面,如果一行里写多个语句那么分号是需要的。例如
Scala 包
定义包
Scala 使用 package 关键字定义包,在Scala将代码定义到某个包中有两种方式:
第一种方法和 Java 一样,在文件的头定义包名,这种方法就后续所有代码都放在该包中。 比如:
第二种方法有些类似 C#,如:
第二种方法,可以在一个文件中定义多个包。
引用
Scala 使用 import 关键字引用包。
import语句可以出现在任何地方,而不是只能在文件顶部。import的效果从开始延伸到语句块的结束。这可以大幅减少名称冲突的可能性。
如果想要引入包中的几个成员,可以使用selector(选取器):
Scala 数据类型
Scala 与 Java有着相同的数据类型,下表列出了 Scala 支持的数据类型:
上表中列出的数据类型都是对象,也就是说scala没有java中的原生类型。在scala是可以对数字等基础类型调用方法的。
Scala 转义字符
下表列出了常见的转义字符:
0 到 255 间的 Unicode 字符可以用一个八进制转义序列来表示,即反斜线‟\‟后跟 最多三个八进制。
在字符或字符串中,反斜线和后面的字符序列不能构成一个合法的转义序列将会导致 编译错误。
以下实例演示了一些转义字符的使用:
Scala 变量
变量是一种使用方便的占位符,用于引用计算机内存地址,变量创建后会占用一定的内存空间。
基于变量的数据类型,操作系统会进行内存分配并且决定什么将被储存在保留内存中。因此,通过给变量分配不同的数据类型,你可以在这些变量中存储整数,小数或者字母。
变量声明
在学习如何声明变量与常量之前,我们先来了解一些变量与常量。
在 Scala 中,使用关键词 "var" 声明变量,使用关键词 "val" 声明常量。
声明变量实例如下:
以上定义了变量 myVar,我们可以修改它。
声明常量实例如下:
以上定义了常量 myVal,它是不能修改的。如果程序尝试修改常量 myVal 的值,程序将会在编译时报错。
变量类型声明
变量的类型在变量名之后等号之前声明。定义变量的类型的语法格式如下:
变量声明一定需要初始值,否则会报错。
变量类型引用
在 Scala 中声明变量和常量不一定要指明数据类型,在没有指明数据类型的情况下,其数据类型是通过变量或常量的初始值推断出来的。
所以,如果在没有指明数据类型的情况下声明变量或常量必须要给出其初始值,否则将会报错。
以上实例中,myVar 会被推断为 Int 类型,myVal 会被推断为 String 类型。
Scala 多个变量声明
Scala 支持多个变量的声明:
如果方法返回值是元组,我们可以使用 val 来声明一个元组:
Scala 访问修饰符
Scala 访问修饰符基本和Java的一样,分别有:private,protected,public。
如果没有指定访问修饰符,默认情况下,Scala 对象的访问级别都是 public。
Scala 中的 private 限定符,比 Java 更严格,在嵌套类情况下,外层类甚至不能访问被嵌套类的私有成员。
私有(Private)成员
用 private 关键字修饰,带有此标记的成员仅在包含了成员定义的类或对象内部可见,同样的规则还适用内部类。
(new Inner).f( ) 访问不合法是因为 f 在 Inner 中被声明为 private,而访问不在类 Inner 之内。
但在 InnerMost 里访问 f 就没有问题的,因为这个访问包含在 Inner 类之内。
Java中允许这两种访问,因为它允许外部类访问内部类的私有成员。
保护(Protected)成员
在 scala 中,对保护(Protected)成员的访问比 java 更严格一些。因为它只允许保护成员在定义了该成员的的类的子类中被访问。而在java中,用protected关键字修饰的成员,除了定义了该成员的类的子类可以访问,同一个包里的其他类也可以进行访问。
上例中,Sub 类对 f 的访问没有问题,因为 f 在 Super 中被声明为 protected,而 Sub 是 Super 的子类。相反,Other 对 f 的访问不被允许,因为 other 没有继承自 Super。而后者在 java 里同样被认可,因为 Other 与 Sub 在同一包里。
公共(Public)成员
Scala中,如果没有指定任何的修饰符,则默认为 public。这样的成员在任何地方都可以被访问。
作用域保护
Scala中,访问修饰符可以通过使用限定词强调。格式为:
这里的x指代某个所属的包、类或单例对象。如果写成private[x],读作"这个成员除了对[…]中的类或[…]中的包中的类及它们的伴生对像可见外,对其它所有类都是private。
这种技巧在横跨了若干包的大型项目中非常有用,它允许你定义一些在你项目的若干子包中可见但对于项目外部的客户却始终不可见的东西。
上述例子中,类Navigator被标记为private[bobsrockets]就是说这个类对包含在bobsrockets包里的所有的类和对象可见。
比如说,从Vehicle对象里对Navigator的访问是被允许的,因为对象Vehicle包含在包launch中,而launch包在bobsrockets中,相反,所有在包bobsrockets之外的代码都不能访问类Navigator。
Scala 运算符
一个运算符是一个符号,用于告诉编译器来执行指定的数学运算和逻辑运算。
Scala 含有丰富的内置运算符,包括以下几种类型:
算术运算符
下表列出了 Scala 支持的算术运算符。
假定变量 A 为 10,B 为 20:
关系运算符
下表列出了 Scala 支持的关系运算符。
假定变量 A 为 10,B 为 20:
逻辑运算符
下表列出了 Scala 支持的逻辑运算符。
假定变量 A 为 1,B 为 0:
位运算符
位运算符用来对二进制位进行操作,~,&,|,^分别为取反,按位与与,按位与或,按位与异或运算,如下表实例:
Scala 中的按位运算法则如下:
赋值运算符
以下列出了 Scala 语言支持的赋值运算符:
Scala IF...ELSE 语句
Scala IF...ELSE 语句是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块。
if 语句
if 语句有布尔表达式及之后的语句块组成。
语法
if 语句的语法格式如下:
如果布尔表达式为 true 则执行大括号内的语句块,否则跳过大括号内的语句块,执行大括号之后的语句块。
实例
if...else 语句
if 语句后可以紧跟 else 语句,else 内的语句块可以在布尔表达式为 false 的时候执行。
语法
if...else 的语法格式如下:
实例
if...else if...else 语句
if 语句后可以紧跟 else if...else 语句,在多个条件判断语句的情况下很有用。
语法
if...else if...else 语法格式如下:
if...else 嵌套语句
if...else 嵌套语句可以实现在 if 语句内嵌入一个或多个 if 语句。
语法
if...else 嵌套语句语法格式如下:
else if...else 的嵌套语句 类似 if...else 嵌套语句。
实例
Scala 循环
有的时候,我们可能需要多次执行同一块代码。一般情况下,语句是按顺序执行的:函数中的第一个语句先执行,接着是第二个语句,依此类推。
编程语言提供了更为复杂执行路径的多种控制结构。
循环类型
Scala 语言提供了以下几种循环类型。
循环控制语句
循环控制语句改变你代码的执行顺序,通过它你可以实现代码的跳转。Scala 以下几种循环控制语句:
Scala 不支持 break 或 continue 语句,但从 2.8 版本后提供了一种中断循环的方式。
无限循环
如果条件永远为 true,则循环将变成无限循环。我们可以使用 while 语句来实现无限循环:
以上代码执行后循环会永久执行下去,你可以使用 Ctrl + C 键来中断无限循环。
Scala 方法与函数
Scala 有方法与函数,二者在语义上的区别很小。Scala 方法是类的一部分,而函数是一个对象可以赋值给一个变量。换句话来说在类中定义的函数即是方法。
Scala 中的方法跟 Java 的类似,方法是组成类的一部分。
Scala 中的函数则是一个完整的对象,Scala 中的函数其实就是继承了 Trait 的类的对象。
Scala 中使用 val 语句可以定义函数,def 语句定义方法。
方法声明
Scala 方法声明格式如下:
如果你不写等于号和方法主体,那么方法会被隐式声明为抽象(abstract),包含它的类型于是也是一个抽象类型。
方法定义
方法定义由一个 def 关键字开始,紧接着是可选的参数列表,一个冒号 : 和方法的返回类型,一个等于号 = ,最后是方法的主体。
Scala 方法定义格式如下:
以上代码中 return type 可以是任意合法的 Scala 数据类型。参数列表中的参数可以使用逗号分隔。
以下方法的功能是将两个传入的参数相加并求和:
如果方法没有返回值,可以返回为 Unit,这个类似于 Java 的 void, 实例如下:
方法调用
Scala 提供了多种不同的方法调用方式:
以下是调用方法的标准格式:
如果方法使用了实例的对象来调用,我们可以使用类似java的格式 (使用 . 号):
以上实例演示了定义与调用方法的实例:
执行以上代码,输出结果为:
Scala 也是一种函数式语言,所以函数是 Scala 语言的核心。
Scala 指定函数参数名
一般情况下函数调用参数,就按照函数定义时的参数顺序一个个传递。但是我们也可以通过指定函数参数名,并且不需要按照顺序向函数传递参数,实例如下:
Scala 函数传名调用(call-by-name)
Scala的解释器在解析函数参数(function arguments)时有两种方式:
在进入函数内部前,传值调用方式就已经将参数表达式的值计算完毕,而传名调用是在函数内部进行参数表达式的值计算的。
这就造成了一种现象,每次使用传名调用时,解释器都会计算一次表达式的值。
以上实例中我们声明了 delayed 方法, 该方法在变量名和变量类型使用 => 符号来设置传名调用。
Scala 函数 - 可变参数
Scala 允许你指明函数的最后一个参数可以是重复的,即我们不需要指定函数参数的个数,可以向函数传入可变长度参数列表。
Scala 通过在参数的类型之后放一个星号来设置可变参数(可重复的参数)。例如:
Scala 递归函数
递归函数在函数式编程的语言中起着重要的作用。
Scala 同样支持递归函数。
递归函数意味着函数可以调用它本身。
以上实例使用递归函数来计算阶乘:
执行以上代码,输出结果为:
Scala 函数 - 默认参数值
Scala 可以为函数参数指定默认参数值,使用了默认参数,你在调用函数的过程中可以不需要传递参数,这时函数就会调用它的默认参数值,如果传递了参数,则传递值会取代默认值。实例如下:
执行以上代码,输出结果为:
Scala 高阶函数
高阶函数(Higher-Order Function)就是操作其他函数的函数。
Scala 中允许使用高阶函数, 高阶函数可以使用其他函数作为参数,或者使用函数作为输出结果。
以下实例中,apply() 函数使用了另外一个函数 f 和 值 v 作为参数,而函数 f 又调用了参数 v:
执行以上代码,输出结果为:
Scala 函数嵌套
我们可以在 Scala 函数内定义函数,定义在函数内的函数称之为局部函数。
以下实例我们实现阶乘运算,并使用内嵌函数:
执行以上代码,输出结果为:
Scala匿名函数
Scala 中定义匿名函数的语法很简单,箭头左边是参数列表,右边是函数体。
使用匿名函数后,我们的代码变得更简洁了。
下面的表达式就定义了一个接受一个Int类型输入参数的匿名函数:
上述定义的匿名函数,其实是下面这种写法的简写:
以上实例的 inc 现在可作为一个函数,使用方式如下:
同样我们可以在匿名函数中定义多个参数:
mul 现在可作为一个函数,使用方式如下:
我们也可以不给匿名函数设置参数,如下所示:
userDir 现在可作为一个函数,使用方式如下:
实例
将以上代码保持到 Demo.scala 文件中,执行以下命令:
输出结果为:
Scala 偏应用函数
Scala 偏应用函数是一种表达式,你不需要提供函数需要的所有参数,只需要提供部分,或不提供所需参数。
如下实例,我们打印日志信息:
执行以上代码,输出结果为:
实例中,log() 方法接收两个参数:date 和 message。我们在程序执行时调用了三次,参数 date 值都相同,message 不同。
我们可以使用偏应用函数优化以上方法,绑定第一个 date 参数,第二个参数使用下划线(_)替换缺失的参数列表,并把这个新的函数值的索引的赋给变量。以上实例修改如下:
执行以上代码,输出结果为:
Scala 函数柯里化
柯里化(Currying)指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数为参数的函数。
实例
首先我们定义一个函数:
那么我们应用的时候,应该是这样用:add(1,2)
现在我们把这个函数变一下形:
那么我们应用的时候,应该是这样用:add(1)(2),最后结果都一样是3,这种方式(过程)就叫柯里化。
实现过程
add(1)(2) 实际上是依次调用两个普通函数(非柯里化函数),第一次调用使用一个参数 x,返回一个函数类型的值,第二次使用参数y调用这个函数类型的值。
实质上最先演变成这样一个方法:
那么这个函数是什么意思呢? 接收一个x为参数,返回一个匿名函数,该匿名函数的定义是:接收一个Int型参数y,函数体为x+y。现在我们来对这个方法进行调用。
返回一个result,那result的值应该是一个匿名函数:(y:Int)=>1+y
所以为了得到结果,我们继续调用result。
最后打印出来的结果就是3。
完整实例
下面是一个完整实例:
执行以上代码,输出结果为:
Scala 闭包
闭包是一个函数,返回值依赖于声明在函数外部的一个或多个变量。
闭包通常来讲可以简单的认为是可以访问一个函数里面局部变量的另外一个函数。
如下面这段匿名的函数:
函数体内有一个变量 i,它作为函数的一个参数。如下面的另一段代码:
在 multiplier 中有两个变量:i 和 factor。其中的一个 i 是函数的形式参数,在 multiplier 函数被调用时,i 被赋予一个新的值。然而,factor不是形式参数,而是自由变量,考虑下面代码:
这里我们引入一个自由变量 factor,这个变量定义在函数外面。
这样定义的函数变量 multiplier 成为一个"闭包",因为它引用到函数外面定义的变量,定义这个函数的过程是将这个自由变量捕获而构成一个封闭的函数。
完整实例
执行以上代码,输出结果为:
Scala 集合
Scala提供了一套很好的集合实现,提供了一些集合类型的抽象。Scala 集合分为可变的和不可变的集合。
可变集合可以在适当的地方被更新或扩展。这意味着你可以修改,添加,移除一个集合的元素。
而不可变集合类,相比之下,永远不会改变。不过,你仍然可以模拟添加,移除或更新操作。但是这些操作将在每一种情况下都返回一个新的集合,同时使原来的集合不发生改变。
List(列表)
Scala 列表类似于数组,它们所有元素的类型都相同,但是它们也有所不同:列表是不可变的,值一旦被定义了就不能改变,其次列表 具有递归的结构(也就是链接表结构)而数组不是。
列表是有序的,可以包含重复项,不可变。
列表的元素类型 T 可以写成 List[T]。例如,以下列出了多种类型的列表:
构造列表的两个基本单位是 Nil 和 ::
Nil 也可以表示为一个空列表。
以上实例我们可以写成如下所示:
Set(集合)
Scala Set(集合)是没有重复的对象集合,所有的元素都是唯一的。
Scala 集合分为可变的和不可变的集合。集合无序且不可包含重复项。
默认情况下,Scala 使用的是不可变集合,如果你想使用可变集合,需要引用 scala.collection.mutable.Set 包。
默认引用 scala.collection.immutable.Set,不可变集合实例如下:
如果需要使用可变集合需要引入 scala.collection.mutable.Set:
Map(映射)
Map(映射)是一种可迭代的键值对(key/value)结构。
所有的值都可以通过键来获取。
Map 中的键都是唯一的。
Map 也叫哈希表(Hash tables)。
Map 有两种类型,可变与不可变,区别在于可变对象可以修改它,而不可变对象不可以。
默认情况下 Scala 使用不可变 Map。如果你需要使用可变集合,你需要显式的引入 import scala.collection.mutable.Map 类
在 Scala 中 你可以同时使用可变与不可变 Map,不可变的直接使用 Map,可变的使用 mutable.Map。以下实例演示了不可变 Map 的应用:
定义 Map 时,需要为键值对定义类型。如果需要添加 key-value 对,可以使用 + 号,如下所示:
Map 基本操作
Scala Map 有三个基本操作:
实例
以下实例演示了以上三个方法的基本应用:
执行以上代码,输出结果为:
Map 合并
你可以使用 ++ 运算符或 Map.++() 方法来连接两个 Map,Map 合并时会移除重复的 key。以下演示了两个 Map 合并的实例:
执行以上代码,输出结果为:
元组 Tuple
元组在不使用类的情况下,将元素组合起来形成简单的逻辑集合。
与样本类不同,元组不能通过名称获取字段,而是使用位置下标来读取对象;而且这个下标基于1,而不是基于0。
元组可以很好得与模式匹配相结合。
在创建两个元素的元组时,可以使用特殊语法:
->
与列表一样,元组也是不可变的,但与列表不同的是元组可以包含不同类型的元素。
元组的值是通过将单个的值包含在圆括号中构成的。例如:
以上实例在元组中定义了三个元素,对应的类型分别为[Int, Double, java.lang.String]。
此外我们也可以使用以上方式来定义:
元组的实际类型取决于它的元素的类型,比如 (99, "runoob") 是 Tuple2[Int, String]。 ('u', 'r', "the", 1, 4, "me") 为 Tuple6[Char, Char, String, Int, Int, String]。
目前 Scala 支持的元组最大长度为 22。对于更大长度你可以使用集合,或者扩展元组。
访问元组的元素可以通过数字索引,如下一个元组:
我们可以使用 t._1 访问第一个元素, t._2 访问第二个元素,如下所示:
执行以上代码,输出结果为:
Option(选项)
Scala Option(选项)类型用来表示一个值是可选的(有值或无值)。
Option[T] 是一个类型为 T 的可选值的容器: 如果值存在, Option[T] 就是一个 Some[T] ,如果不存在, Option[T] 就是对象 None 。
接下来我们来看一段代码:
在上面的代码中,myMap 一个是一个 Key 的类型是 String,Value 的类型是 String 的 hash map,但不一样的是他的 get() 返回的是一个叫 Option[String] 的类别。
Scala 使用 Option[String] 来告诉你:「我会想办法回传一个 String,但也可能没有 String 给你」。
myMap 里并没有 key2 这笔数据,get() 方法返回 None。
Option 有两个子类别,一个是 Some,一个是 None,当他回传 Some 的时候,代表这个函式成功地给了你一个 String,而你可以透过 get() 这个函式拿到那个 String,如果他返回的是 None,则代表没有字符串可以给你。
getOrElse() 方法
你可以使用 getOrElse() 方法来获取元组中存在的元素或者使用其默认的值,实例如下:
执行以上代码,输出结果为:
isEmpty() 方法
你可以使用 isEmpty() 方法来检测元组中的元素是否为 None,实例如下:
执行以上代码,输出结果为:
The text was updated successfully, but these errors were encountered: