Scala学习笔记

7/18/2020 scala

# 简介

Scala 是 Scalable Language 的简写,是一门多范式的编程语言

联邦理工学院洛桑(EPFL)的Martin Odersky于2001年基于Funnel的工作开始设计Scala。

Funnel是把函数式编程思想和Petri网相结合的一种编程语言。

# Scala特性

# 面向对象特性

Scala是一种纯面向对象的语言,每个值都是对象。对象的数据类型以及行为由类和特质描述。

类抽象机制的扩展有两种途径:一种途径是子类继承,另一种途径是灵活的混入机制。这两种途径能避免多重继承的种种问题。

# 函数式编程

Scala也是一种函数式语言,其函数也能当成值来使用。Scala提供了轻量级的语法用以定义匿名函数,支持高阶函数,允许嵌套多层函数,并支持柯里化。Scala的case class及其内置的模式匹配相当于函数式编程语言中常用的代数类型。

更进一步,程序员可以利用Scala的模式匹配,编写类似正则表达式的代码处理XML数据。

# 静态类型

Scala具备类型系统,通过编译时检查,保证代码的安全性和一致性。

# 扩展性

Scala的设计秉承一项事实,即在实践中,某个领域特定的应用程序开发往往需要特定于该领域的语言扩展。Scala提供了许多独特的语言机制,可以以库的形式轻易无缝添加新的语言结构:

  • 任何方法可用作前缀或后缀操作符
  • 可以根据预期类型自动构造闭包。

# 并发性

Scala使用Actor作为其并发模型,Actor是类似线程的实体,通过邮箱发收消息。Actor可以复用线程,因此可以在程序中可以使用数百万个Actor,而线程只能创建数千个。在2.10之后的版本中,使用Akka作为其默认Actor实现。

# 谁使用了 Scala

  • 2009年4月,Twitter宣布他们已经把大部分后端程序从Ruby迁移到Scala,其余部分也打算要迁移。
  • 此外,Wattzon已经公开宣称,其整个平台都已经是基于Scala基础设施编写的。
  • 瑞银集团把Scala用于一般产品中。
  • Coursera把Scala作为服务器语言使用。

# 基础语法

Scala 与 Java 的最大区别是:Scala 语句末尾的分号 ; 是可选的。

scala的helloworld.scala

object HelloWorld {
   /* 这是我的第一个 Scala 程序
    * 以下程序将输出'Hello World!' 
    */
   def main(args: Array[String]) {
      println("Hello, world!") // 输出 Hello World
   }
}
1
2
3
4
5
6
7
8

# 基本语法需要注意以下几点:

  • 区分大小写 - Scala是大小写敏感的,这意味着标识Hello 和 hello在Scala中会有不同的含义。

  • 类名 - 对于所有的类名的第一个字母要大写。 如果需要使用几个单词来构成一个类的名称,每个单词的第一个字母要大写。

    示例:class MyFirstScalaClass

  • 方法名称 - 所有的方法名称的第一个字母用小写。 如果若干单词被用于构成方法的名称,则每个单词的第一个字母应大写。

    示例:def myMethodName()

  • 程序文件名 - 程序文件的名称应该与对象名称完全匹配(新版本不需要了,但建议保留这种习惯)。 保存文件时,应该保存它使用的对象名称(记住Scala是区分大小写),并追加".scala"为文件扩展名。 (如果文件名和对象名称不匹配,程序将无法编译)。

    示例: 假设"HelloWorld"是对象的名称。那么该文件应保存为'HelloWorld.scala"

  • def main(args: Array[String]) - Scala程序从main()方法开始处理,这是每一个Scala程序的强制程序入口部分。

# 标识符

Scala 可以使用两种形式的标志符,字符数字和符号。

字符数字使用字母或是下划线开头,后面可以接字母或是数字,符号"$"在 Scala 中也看作为字母。然而以"$"开头的标识符为保留的 Scala 编译器产生的标志符使用,应用程序应该避免使用"$"开始的标识符,以免造成冲突。

Scala 的命名规则采用和 Java 类似的 camel 命名规则,首字符小写,比如 toString。类名的首字符还是使用大写。此外也应该避免使用以下划线结尾的标志符以避免冲突。符号标志符包含一个或多个符号,如+,:,? 等

# 关键字

abstract case catch class
def do else extends
false final finally for
forSome if implicit import
lazy match new null
object override package private
protected return sealed super
this throw trait try
true type val var
while with yield
- : = =>
<- <: <% >:
# @

# 注释

// 或者/* */

# 空行和空格

一行中只有空格或者带有注释,Scala 会认为其是空行,会忽略它。标记可以被空格或者注释来分割。

# 换行符

Scala是面向行的语言,语句可以用分号(;)结束或换行符。Scala 程序里,语句末尾的分号通常是可选的。如果你愿意可以输入一个,但若一行里仅 有一个语句也可不写。另一方面,如果一行里写多个语句那么分号是需要的。例如

val s = "weekdawn"; println(s)
1

# Scala包

Scala 使用 package 关键字定义包,在Scala将代码定义到某个包中有两种方式:

第一种方法和 Java 一样,在文件的头定义包名,这种方法就后续所有代码都放在该包中。 比如:

package com.runoob
class HelloWorld
1
2

第二种方法有些类似 C#,如:

package com.runoob {
  class HelloWorld 
}
1
2
3

第二种方法,可以在一个文件中定义多个包。

# 引用

Scala 使用 import 关键字引用包。

import java.awt.Color  // 引入Color
 
import java.awt._  // 引入包内所有成员
 
def handler(evt: event.ActionEvent) { // java.awt.event.ActionEvent
  ...  // 因为引入了java.awt,所以可以省去前面的部分
}
1
2
3
4
5
6
7

import语句可以出现在任何地方,而不是只能在文件顶部。import的效果从开始延伸到语句块的结束。这可以大幅减少名称冲突的可能性。

如果想要引入包中的几个成员,可以使用selector(选取器):

import java.awt.{Color, Font}
 
// 重命名成员
import java.util.{HashMap => JavaHashMap}
 
// 隐藏成员
import java.util.{HashMap => _, _} // 引入了util包的所有成员,但是HashMap被隐藏了
1
2
3
4
5
6
7

**注意:**默认情况下,Scala 总会引入 java.lang._ 、 scala._ 和 Predef._,这里也能解释,为什么以scala开头的包,在使用时都是省去scala.的。

# Scala数据类型

数据类型 描述
Byte 8位有符号补码整数。数值区间为 -128 到 127
Short 16位有符号补码整数。数值区间为 -32768 到 32767
Int 32位有符号补码整数。数值区间为 -2147483648 到 2147483647
Long 64位有符号补码整数。数值区间为 -9223372036854775808 到 9223372036854775807
Float 32 位, IEEE 754 标准的单精度浮点数
Double 64 位 IEEE 754 标准的双精度浮点数
Char 16位无符号Unicode字符, 区间值为 U+0000 到 U+FFFF
String 字符序列
Boolean true或false
Unit 表示无值,和其他语言中void等同。用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()。
Null null 或空引用
Nothing Nothing类型在Scala的类层级的最低端;它是任何其他类型的子类型。
Any Any是所有其他类的超类
AnyRef AnyRef类是Scala里所有引用类(reference class)的基类

# Scala基础字面量

# 整型字面量

整型字面量用于 Int 类型,如果表示 Long,可以在数字后面添加 L 或者小写 l 作为后缀。:

0
035
21 
0xFFFFFFFF 
0777L
1
2
3
4
5

# 浮点型字面量

如果浮点数后面有f或者F后缀时,表示这是一个Float类型,否则就是一个Double类型的。实例如下:

0.0 
1e30f 
3.14159f 
1.0e100
.1
1
2
3
4
5

# 布尔型字面量

布尔型字面量有 true 和 false。

# 符号字面量

符号字面量被写成: '<标识符> ,这里 <标识符> 可以是任何字母或数字的标识(注意:不能以数字开头)。这种字面量被映射成预定义类scala.Symbol的实例。

如: 符号字面量 'x****scala.Symbol("x")

package scala
final case class Symbol private (name: String) {
   override def toString: String = "'" + name
}
1
2
3
4

# 字符字面量

在 Scala 字符变量使用单引号 ' 来定义,如下:

'a' 
'\u0041'
'\n'
'\t'
1
2
3
4

其中 ** 表示转义字符,其后可以跟 u0041 数字或者 \r\n 等固定的转义字符。

# 字符串字面量

在 Scala 字符串变量使用双引号 " 来定义,如下:

"Hello,\nWorld!"
"菜鸟教程官网:www.runoob.com"
1
2

# 多行字符串的表示方法

多行字符串用三个双引号来表示分隔符,格式为:""" ... """

实例如下:

val foo = """菜鸟教程
www.runoob.com
www.w3cschool.cc
www.runnoob.com
以上三个地址都能访问"""
1
2
3
4
5

# Null 值

空值是 scala.Null 类型。

Scala.Null和scala.Nothing是用统一的方式处理Scala面向对象类型系统的某些"边界情况"的特殊类型。

Null类是null引用对象的类型,它是每个引用类(继承自AnyRef的类)的子类。Null不兼容值类型。

# 转义字符

转义字符 Unicode 描述
\b \u0008 退格(BS) ,将当前位置移到前一列
\t \u0009 水平制表(HT) (跳到下一个TAB位置)
\n \u000a 换行(LF) ,将当前位置移到下一行开头
\f \u000c 换页(FF),将当前位置移到下页开头
\r \u000d 回车(CR) ,将当前位置移到本行开头
" \u0022 代表一个双引号(")字符
' \u0027 代表一个单引号(')字符
\ \u005c 代表一个反斜线字符 ''

# Scala变量

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

声明变量实例如下:

var myVar : String = "Foo"
var myVar : String = "Too"
1
2

以上定义了变量 myVar,我们可以修改它。

声明常量实例如下:

val myVal : String = "Foo"
1

以上定义了常量 myVal,它是不能修改的。如果程序尝试修改常量 myVal 的值,程序将会在编译时报错。

# 变量类型声明

变量的类型在变量名之后等号之前声明。定义变量的类型的语法格式如下:

var VariableName : DataType [=  Initial Value]

或

val VariableName : DataType [=  Initial Value]
1
2
3
4
5

# 变量类型引用

在 Scala 中声明变量和常量不一定要指明数据类型,在没有指明数据类型的情况下,其数据类型是通过变量或常量的初始值推断出来的。

所以,如果在没有指明数据类型的情况下声明变量或常量必须要给出其初始值,否则将会报错。

var myVar = 10;
val myVal = "Hello, Scala!";
1
2

以上实例中,myVar 会被推断为 Int 类型,myVal 会被推断为 String 类型。

# Scala多个变量声明

Scala 支持多个变量的声明:

val xmax, ymax = 100  // xmax, ymax都声明为100
1

如果方法返回值是元组,我们可以使用 val 来声明一个元组:

scala> val pa = (40,"Foo")
pa: (Int, String) = (40,Foo)
1
2

# Scala访问修饰符

Scala 访问修饰符基本和Java的一样,分别有:private,protected,public。

如果没有指定访问修饰符,默认情况下,Scala 对象的访问级别都是 public。

Scala 中的 private 限定符,比 Java 更严格,在嵌套类情况下,外层类甚至不能访问被嵌套类的私有成员。

# Private成员

用 private 关键字修饰,带有此标记的成员仅在包含了成员定义的类或对象内部可见,同样的规则还适用内部类。

class Outer{
    class Inner{
    private def f(){println("f")}
    class InnerMost{
        f() // 正确
        }
    }
    (new Inner).f() //错误
}
1
2
3
4
5
6
7
8
9

# Protected成员

在 scala 中,对保护(Protected)成员的访问比 java 更严格一些。因为它只允许保护成员在定义了该成员的的类的子类中被访问。而在java中,用protected关键字修饰的成员,除了定义了该成员的类的子类可以访问,同一个包里的其他类也可以进行访问。

package p{
class Super{
    protected def f() {println("f")}
    }
    class Sub extends Super{
        f()
    }
    class Other{
        (new Super).f() //错误
    }
}
1
2
3
4
5
6
7
8
9
10
11

# Public成员

Scala中,如果没有指定任何的修饰符,则默认为 public。这样的成员在任何地方都可以被访问。

class Outer {
   class Inner {
      def f() { println("f") }
      class InnerMost {
         f() // 正确
      }
   }
   (new Inner).f() // 正确因为 f() 是 public
}
1
2
3
4
5
6
7
8
9

# Scala运算符

# Scala IF...ELSE

# Scala循环

# For循环

Scala 语言中 for 循环的语法:

for( var x <- Range ){
   statement(s);
}
1
2
3

以上语法中,Range 可以是一个数字区间表示 i to j ,或者 i until j。左箭头 <- 用于为变量 x 赋值。

# 实例

以下是一个使用了 i to j 语法(包含 j)的实例:

object Test {
   def main(args: Array[String]) {
      var a = 0;
      // for 循环
      for( a <- 1 to 10){
         println( "Value of a: " + a );
      }
   }
}
1
2
3
4
5
6
7
8
9

以下是一个使用了 i until j 语法(不包含 j)的实例:

object Test {
   def main(args: Array[String]) {
      var a = 0;
      // for 循环
      for( a <- 1 until 10){
         println( "Value of a: " + a );
      }
   }
}
1
2
3
4
5
6
7
8
9

for 循环 中你可以使用分号 (😉 来设置多个区间,它将迭代给定区间所有的可能值。以下实例演示了两个区间的循环实例:

object Test {
   def main(args: Array[String]) {
      var a = 0;
      var b = 0;
      // for 循环
      for( a <- 1 to 3; b <- 1 to 3){
         println( "Value of a: " + a );
         println( "Value of b: " + b );
      }
   }
}
1
2
3
4
5
6
7
8
9
10
11

# for 循环集合

for 循环集合的语法如下:

for( var x <- List ){
   statement(s);
}
1
2
3

以上语法中, List 变量是一个集合,for 循环会迭代所有集合的元素。

# 实例

以下实例将循环数字集合。我们使用 List() 来创建集合。再以后章节我们会详细介绍集合。

object Test {
   def main(args: Array[String]) {
      var a = 0;
      val numList = List(1,2,3,4,5,6);

      // for 循环
      for( a <- numList ){
         println( "Value of a: " + a );
      }
   }
}
1
2
3
4
5
6
7
8
9
10
11

# for 循环过滤

Scala 可以使用一个或多个 if 语句来过滤一些元素。

以下是在 for 循环中使用过滤器的语法。

for( var x <- List
      if condition1; if condition2...
   ){
   statement(s);
1
2
3
4

你可以使用分号(;)来为表达式添加一个或多个的过滤条件。

# 实例

以下是 for 循环中过滤的实例:

object Test {
   def main(args: Array[String]) {
      var a = 0;
      val numList = List(1,2,3,4,5,6,7,8,9,10);

      // for 循环
      for( a <- numList
           if a != 3; if a < 8 ){
         println( "Value of a: " + a );
      }
   }
}
1
2
3
4
5
6
7
8
9
10
11
12

# for 使用 yield

你可以将 for 循环的返回值作为一个变量存储。语法格式如下:

var retVal = for{ var x <- List
     if condition1; if condition2...
}yield x
1
2
3

注意大括号中用于保存变量和条件,retVal 是变量, 循环中的 yield 会把当前的元素记下来,保存在集合中,循环结束后将返回该集合。

# 实例

以下实例演示了 for 循环中使用 yield:

object Test {
   def main(args: Array[String]) {
      var a = 0;
      val numList = List(1,2,3,4,5,6,7,8,9,10);

      // for 循环
      var retVal = for{ a <- numList 
                        if a != 3; if a < 8
                      }yield a

      // 输出返回值
      for( a <- retVal){
         println( "Value of a: " + a );
      }
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# Scala方法与函数

Scala 有方法与函数,二者在语义上的区别很小。Scala 方法是类的一部分,而函数是一个对象可以赋值给一个变量。换句话来说在类中定义的函数即是方法。

Scala 中的方法跟 Java 的类似,方法是组成类的一部分。

Scala 中的函数则是一个完整的对象,Scala 中的函数其实就是继承了 Trait 的类的对象。

Scala 中使用 val 语句可以定义函数,def 语句定义方法。

class Test{
  def m(x: Int) = x + 3
  val f = (x: Int) => x + 3
}
1
2
3
4

# 方法声明

Scala 方法声明格式如下:

def functionName ([参数列表]) : [return type]
1

如果你不写等于号和方法主体,那么方法会被隐式声明为抽象(abstract),包含它的类型于是也是一个抽象类型。

# 方法定义

方法定义由一个 def 关键字开始,紧接着是可选的参数列表,一个冒号 : 和方法的返回类型,一个等于号 = ,最后是方法的主体。

Scala 方法定义格式如下:

def functionName ([参数列表]) : [return type] = {
   function body
   return [expr]
}
1
2
3
4

以上代码中 return type 可以是任意合法的 Scala 数据类型。参数列表中的参数可以使用逗号分隔。

以下方法的功能是将两个传入的参数相加并求和:

object add{
   def addInt( a:Int, b:Int ) : Int = {
      var sum:Int = 0
      sum = a + b

      return sum
   }
}
1
2
3
4
5
6
7
8

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

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

# 方法调用

Scala 提供了多种不同的方法调用方式:

以下是调用方法的标准格式:

functionName( 参数列表 )
1

如果方法使用了实例的对象来调用,我们可以使用类似java的格式 (使用 . 号):

[instance.]functionName( 参数列表 )
1

以上实例演示了定义与调用方法的实例:

object Test {
   def main(args: Array[String]) {
        println( "Returned Value : " + addInt(5,7) );
   }
   def addInt( a:Int, b:Int ) : Int = {
      var sum:Int = 0
      sum = a + b

      return sum
   }
}
1
2
3
4
5
6
7
8
9
10
11

# Scala闭包

闭包是一个函数,返回值依赖于声明在函数外部的一个或多个变量。

闭包通常来讲可以简单的认为是可以访问一个函数里面局部变量的另外一个函数。

如下面这段匿名的函数:

val multiplier = (i:Int) => i * 10  
1

函数体内有一个变量 i,它作为函数的一个参数。如下面的另一段代码:

val multiplier = (i:Int) => i * factor
1

在 multiplier 中有两个变量:i 和 factor。其中的一个 i 是函数的形式参数,在 multiplier 函数被调用时,i 被赋予一个新的值。然而,factor不是形式参数,而是自由变量,考虑下面代码:

var factor = 3  
val multiplier = (i:Int) => i * factor  
1
2

这里我们引入一个自由变量 factor,这个变量定义在函数外面。

这样定义的函数变量 multiplier 成为一个"闭包",因为它引用到函数外面定义的变量,定义这个函数的过程是将这个自由变量捕获而构成一个封闭的函数。

完整实例

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

# Scala字符串

以下实例将字符串赋值给一个常量:

object Test {
   val greeting: String = "Hello,World!"

   def main(args: Array[String]) {
      println( greeting )
   }
}
1
2
3
4
5
6
7

以上实例定义了变量 greeting,为字符串常量,它的类型为 String (java.lang.String)。

在 Scala 中,字符串的类型实际上是 Java String,它本身没有 String 类。

在 Scala 中,String 是一个不可变的对象,所以该对象不可被修改。这就意味着你如果修改字符串就会产生一个新的字符串对象。

但其他对象,如数组就是可变的对象。接下来我们会为大家介绍常用的 java.lang.String 方法。

# 创建字符串

创建字符串实例如下:

var greeting = "Hello World!";

或

var greeting:String = "Hello World!";
1
2
3
4
5

你不一定为字符串指定 String 类型,因为 Scala 编译器会自动推断出字符串的类型为 String。

当然我们也可以直接显示的声明字符串为 String 类型,如下实例:

object Test {
   val greeting: String = "Hello, World!"

   def main(args: Array[String]) {
      println( greeting )
   }
}
1
2
3
4
5
6
7

我们前面提到过 String 对象是不可变的,如果你需要创建一个可以修改的字符串,可以使用 String Builder 类,如下实例:

object Test {
   def main(args: Array[String]) {
      val buf = new StringBuilder;
      buf += 'a'
      buf ++= "bcdef"
      println( "buf is : " + buf.toString );
   }
}
1
2
3
4
5
6
7
8

# 字符串长度

我们可以使用 length() 方法来获取字符串长度:

object Test {
   def main(args: Array[String]) {
      var palindrome = "www.runoob.com";
      var len = palindrome.length();
      println( "String Length is : " + len );
   }
}
1
2
3
4
5
6
7

# 字符串连接

String 类中使用 concat() 方法来连接两个字符串:

string1.concat(string2);
1

同样你也可以使用加号(+)来连接:

scala> "weekdawn " + " yoyo"
res1: String = weekdawn yoyo
1
2

# 创建格式化字符串

String 类中你可以使用 printf() 方法来格式化字符串并输出,String format() 方法可以返回 String 对象而不是 PrintStream 对象。以下实例演示了 printf() 方法的使用:

object Test {
   def main(args: Array[String]) {
      var floatVar = 12.456
      var intVar = 2000
      var stringVar = "菜鸟教程!"
      var fs = printf("浮点型变量为 " +
                   "%f, 整型变量为 %d, 字符串为 " +
                   " %s", floatVar, intVar, stringVar)
      println(fs)
   }
}
1
2
3
4
5
6
7
8
9
10
11

# String方法

下表列出了 java.lang.String 中常用的方法,可以在 Scala 中使用:

序号 方法及描述
1 **char charAt(int index)**返回指定位置的字符
2 **int compareTo(Object o)**比较字符串与对象
3 **int compareTo(String anotherString)**按字典顺序比较两个字符串
4 **int compareToIgnoreCase(String str)**按字典顺序比较两个字符串,不考虑大小写
5 **String concat(String str)**将指定字符串连接到此字符串的结尾
6 **boolean contentEquals(StringBuffer sb)**将此字符串与指定的 StringBuffer 比较。
7 **static String copyValueOf(char[] data)**返回指定数组中表示该字符序列的 String
8 **static String copyValueOf(char[] data, int offset, int count)**返回指定数组中表示该字符序列的 String
9 **boolean endsWith(String suffix)**测试此字符串是否以指定的后缀结束
10 **boolean equals(Object anObject)**将此字符串与指定的对象比较
11 **boolean equalsIgnoreCase(String anotherString)**将此 String 与另一个 String 比较,不考虑大小写
12 **byte getBytes()**使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中
13 byte[] getBytes(String charsetName使用指定的字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中
14 **void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)**将字符从此字符串复制到目标字符数组
15 **int hashCode()**返回此字符串的哈希码
16 **int indexOf(int ch)**返回指定字符在此字符串中第一次出现处的索引
17 **int indexOf(int ch, int fromIndex)**返回在此字符串中第一次出现指定字符处的索引,从指定的索引开始搜索
18 **int indexOf(String str)**返回指定子字符串在此字符串中第一次出现处的索引
19 **int indexOf(String str, int fromIndex)**返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始
20 **String intern()**返回字符串对象的规范化表示形式
21 **int lastIndexOf(int ch)**返回指定字符在此字符串中最后一次出现处的索引
22 **int lastIndexOf(int ch, int fromIndex)**返回指定字符在此字符串中最后一次出现处的索引,从指定的索引处开始进行反向搜索
23 **int lastIndexOf(String str)**返回指定子字符串在此字符串中最右边出现处的索引
24 **int lastIndexOf(String str, int fromIndex)**返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索
25 **int length()**返回此字符串的长度
26 **boolean matches(String regex)**告知此字符串是否匹配给定的正则表达式
27 **boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len)**测试两个字符串区域是否相等
28 **boolean regionMatches(int toffset, String other, int ooffset, int len)**测试两个字符串区域是否相等
29 **String replace(char oldChar, char newChar)**返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的
30 String replaceAll(String regex, String replacement使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串
31 **String replaceFirst(String regex, String replacement)**使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串
32 **String[] split(String regex)**根据给定正则表达式的匹配拆分此字符串
33 **String[] split(String regex, int limit)**根据匹配给定的正则表达式来拆分此字符串
34 **boolean startsWith(String prefix)**测试此字符串是否以指定的前缀开始
35 **boolean startsWith(String prefix, int toffset)**测试此字符串从指定索引开始的子字符串是否以指定前缀开始。
36 **CharSequence subSequence(int beginIndex, int endIndex)**返回一个新的字符序列,它是此序列的一个子序列
37 **String substring(int beginIndex)**返回一个新的字符串,它是此字符串的一个子字符串
38 **String substring(int beginIndex, int endIndex)**返回一个新字符串,它是此字符串的一个子字符串
39 **char[] toCharArray()**将此字符串转换为一个新的字符数组
40 **String toLowerCase()**使用默认语言环境的规则将此 String 中的所有字符都转换为小写
41 **String toLowerCase(Locale locale)**使用给定 Locale 的规则将此 String 中的所有字符都转换为小写
42 **String toString()**返回此对象本身(它已经是一个字符串!)
43 **String toUpperCase()**使用默认语言环境的规则将此 String 中的所有字符都转换为大写
44 **String toUpperCase(Locale locale)**使用给定 Locale 的规则将此 String 中的所有字符都转换为大写
45 **String trim()**删除指定字符串的首尾空白符
46 **static String valueOf(primitive data type x)**返回指定类型参数的字符串表示形式

# Scala数组

# 声明数组

以下是 Scala 数组声明的语法格式:

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

或

var z = new Array[String](3)
1
2
3
4
5

# 处理数组

数组的元素类型和数组的大小都是确定的,所以当处理数组元素时候,我们通常使用基本的 for 循环。

以下实例演示了数组的创建,初始化等处理过程:

object Test {
   def main(args: Array[String]) {
      var myList = Array(1.9, 2.9, 3.4, 3.5)
      
      // 输出所有数组元素
      for ( x <- myList ) {
         println( x )
      }

      // 计算数组所有元素的总和
      var total = 0.0;
      for ( i <- 0 to (myList.length - 1)) {
         total += myList(i);
      }
      println("总和为 " + total);

      // 查找数组中的最大元素
      var max = myList(0);
      for ( i <- 1 to (myList.length - 1) ) {
         if (myList(i) > max) max = myList(i);
      }
      println("最大值为 " + max);
    
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 多维数组

多维数组一个数组中的值可以是另一个数组,另一个数组的值也可以是一个数组。矩阵与表格是我们常见的二维数组。

以上是一个定义了二维数组的实例:

var myMatrix = ofDim[Int](3,3)
1

实例中数组中包含三个数组元素,每个数组元素又含有三个值。

接下来我们来看一个二维数组处理的完整实例:

import Array._

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

# 合并数组

以下实例中,我们使用 concat() 方法来合并两个数组,concat() 方法中接受多个数组参数:

import Array._

object Test {
   def main(args: Array[String]) {
      var myList1 = Array(1.9, 2.9, 3.4, 3.5)
      var myList2 = Array(8.9, 7.9, 0.4, 1.5)

      var myList3 =  concat( myList1, myList2)
      
      // 输出所有数组元素
      for ( x <- myList3 ) {
         println( x )
      }
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 创建区间数组

以下实例中,我们使用了 range() 方法来生成一个区间范围内的数组。range() 方法最后一个参数为步长,默认为 1:

import Array._

object Test {
   def main(args: Array[String]) {
      var myList1 = range(10, 20, 2)
      var myList2 = range(10,20)

      // 输出所有数组元素
      for ( x <- myList1 ) {
         print( " " + x )
      }
      println()
      for ( x <- myList2 ) {
         print( " " + x )
      }
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# Scala数组方法

下表中为 Scala 语言中处理数组的重要方法,使用它前我们需要使用 import Array._ 引入包。

序号 方法和描述
1 **def apply( x: T, xs: T* ): Array[T]**创建指定对象 T 的数组, T 的值可以是 Unit, Double, Float, Long, Int, Char, Short, Byte, Boolean。
2 **def concat[T]( xss: Array[T]* ): Array[T]**合并数组
3 def copy( src: AnyRef, srcPos: Int, dest: AnyRef, destPos: Int, length: Int ): Unit复制一个数组到另一个数组上。相等于 Java's System.arraycopy(src, srcPos, dest, destPos, length)。
4 **def empty[T]: Array[T]**返回长度为 0 的数组
5 def iterate[T]( start: T, len: Int )( f: (T) => T ): Array[T]返回指定长度数组,每个数组元素为指定函数的返回值。以上实例数组初始值为 0,长度为 3,计算函数为a=>a+1scala> Array.iterate(0,3)(a=>a+1) res1: Array[Int] = Array(0, 1, 2)
6 **def fill[T]( n: Int )(elem: => T): Array[T]**返回数组,长度为第一个参数指定,同时每个元素使用第二个参数进行填充。
7 **def fill[T]( n1: Int, n2: Int )( elem: => T ): Array[Array[T]]**返回二数组,长度为第一个参数指定,同时每个元素使用第二个参数进行填充。
8 **def ofDim[T]( n1: Int ): Array[T]**创建指定长度的数组
9 **def ofDim[T]( n1: Int, n2: Int ): Array[Array[T]]**创建二维数组
10 **def ofDim[T]( n1: Int, n2: Int, n3: Int ): Array[Array[Array[T]]]**创建三维数组
11 **def range( start: Int, end: Int, step: Int ): Array[Int]**创建指定区间内的数组,step 为每个元素间的步长
12 **def range( start: Int, end: Int ): Array[Int]**创建指定区间内的数组
13 **def tabulate[T]( n: Int )(f: (Int)=> T): Array[T]**返回指定长度数组,每个数组元素为指定函数的返回值,默认从 0 开始。以上实例返回 3 个元素:scala> Array.tabulate(3)(a => a + 5) res0: Array[Int] = Array(5, 6, 7)
14 **def tabulate[T]( n1: Int, n2: Int )( f: (Int, Int ) => T): Array[Array[T]]**返回指定长度的二维数组,每个数组元素为指定函数的返回值,默认从 0 开始。

# Scala集合

# Scala List

Scala 列表类似于数组,它们所有元素的类型都相同,但是它们也有所不同:列表是不可变的,值一旦被定义了就不能改变,其次列表 具有递归的结构(也就是链接表结构)而数组不是。。

列表的元素类型 T 可以写成 List[T]。例如,以下列出了多种类型的列表:

// 字符串列表
val site: List[String] = List("Runoob", "Google", "Baidu")

// 整型列表
val nums: List[Int] = List(1, 2, 3, 4)

// 空列表
val empty: List[Nothing] = List()

// 二维列表
val dim: List[List[Int]] =
   List(
      List(1, 0, 0),
      List(0, 1, 0),
      List(0, 0, 1)
   )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

构造列表的两个基本单位是 Nil::

Nil 也可以表示为一个空列表。

以上实例我们可以写成如下所示:

// 字符串列表
val site = "Runoob" :: ("Google" :: ("Baidu" :: Nil))

// 整型列表
val nums = 1 :: (2 :: (3 :: (4 :: Nil)))

// 空列表
val empty = Nil

// 二维列表
val dim = (1 :: (0 :: (0 :: Nil))) ::
          (0 :: (1 :: (0 :: Nil))) ::
          (0 :: (0 :: (1 :: Nil))) :: Nil
1
2
3
4
5
6
7
8
9
10
11
12
13

# 列表基本操作

Scala列表有三个基本操作:

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

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

object Test {
   def main(args: Array[String]) {
      val site = "Runoob" :: ("Google" :: ("Baidu" :: Nil))
      val nums = Nil

      println( "第一网站是 : " + site.head )
      println( "最后一个网站是 : " + site.tail )
      println( "查看列表 site 是否为空 : " + site.isEmpty )
      println( "查看 nums 是否为空 : " + nums.isEmpty )
   }
}
1
2
3
4
5
6
7
8
9
10
11

执行以上代码,输出结果为:

$ vim Test.scala 
$ scala Test.scala 
第一网站是 : Runoob
最后一个网站是 : List(Google, Baidu)
查看列表 site 是否为空 : false
查看 nums 是否为空 : true
1
2
3
4
5
6

# 连接列表

你可以使用 ::: 运算符或 List.::😦) 方法或 List.concat() 方法来连接两个或多个列表。实例如下:

object Test {
   def main(args: Array[String]) {
      val site1 = "Runoob" :: ("Google" :: ("Baidu" :: Nil))
      val site2 = "Facebook" :: ("Taobao" :: Nil)

      // 使用 ::: 运算符
      var fruit = site1 ::: site2
      println( "site1 ::: site2 : " + fruit )
      
      // 使用 List.:::() 方法
      fruit = site1.:::(site2)
      println( "site1.:::(site2) : " + fruit )

      // 使用 concat 方法
      fruit = List.concat(site1, site2)
      println( "List.concat(site1, site2) : " + fruit  )
      

   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

执行以上代码,输出结果为:

$ vim Test.scala 
$ scala Test.scala 
site1 ::: site2 : List(Runoob, Google, Baidu, Facebook, Taobao)
site1.:::(site2) : List(Facebook, Taobao, Runoob, Google, Baidu)
List.concat(site1, site2) : List(Runoob, Google, Baidu, Facebook, Taobao)
1
2
3
4
5

# List.fill()

我们可以使用 List.fill() 方法来创建一个指定重复数量的元素列表:

object Test {
   def main(args: Array[String]) {
      val site = List.fill(3)("Runoob") // 重复 Runoob 3次
      println( "site : " + site  )

      val num = List.fill(10)(2)         // 重复元素 2, 10 次
      println( "num : " + num  )
   }
}
1
2
3
4
5
6
7
8
9

执行以上代码,输出结果为:

$ vim Test.scala 
$ scala Test.scala 
site : List(Runoob, Runoob, Runoob)
num : List(2, 2, 2, 2, 2, 2, 2, 2, 2, 2)
1
2
3
4

# List.tabulate()

List.tabulate() 方法是通过给定的函数来创建列表。

方法的第一个参数为元素的数量,可以是二维的,第二个参数为指定的函数,我们通过指定的函数计算结果并返回值插入到列表中,起始值为 0,实例如下:

object Test {
   def main(args: Array[String]) {
      // 通过给定的函数创建 5 个元素
      val squares = List.tabulate(6)(n => n * n)
      println( "一维 : " + squares  )

      // 创建二维列表
      val mul = List.tabulate( 4,5 )( _ * _ )      
      println( "多维 : " + mul  )
   }
}
1
2
3
4
5
6
7
8
9
10
11

执行以上代码,输出结果为:

$ vim Test.scala 
$ scala Test.scala 
一维 : List(0, 1, 4, 9, 16, 25)
多维 : List(List(0, 0, 0, 0, 0), List(0, 1, 2, 3, 4), List(0, 2, 4, 6, 8), List(0, 3, 6, 9, 12))
1
2
3
4

# List.reverse

List.reverse 用于将列表的顺序反转,实例如下:

object Test {
   def main(args: Array[String]) {
      val site = "Runoob" :: ("Google" :: ("Baidu" :: Nil))
      println( "site 反转前 : " + site )

      println( "site 反转后 : " + site.reverse )
   }
}
1
2
3
4
5
6
7
8

执行以上代码,输出结果为:

$ vim Test.scala 
$ scala Test.scala 
site 反转前 : List(Runoob, Google, Baidu)
site 反转后 : List(Baidu, Google, Runoob)
1
2
3
4

# Scala Set

Scala Set(集合)是没有重复的对象集合,所有的元素都是唯一的。

Scala 集合分为可变的和不可变的集合。

默认情况下,Scala 使用的是不可变集合,如果你想使用可变集合,需要引用 scala.collection.mutable.Set 包。

默认引用 scala.collection.immutable.Set,不可变集合实例如下:

val set = Set(1,2,3)
println(set.getClass.getName) // 

println(set.exists(_ % 2 == 0)) //true
println(set.drop(1)) //Set(2,3)
1
2
3
4
5

如果需要使用可变集合需要引入 scala.collection.mutable.Set:

import scala.collection.mutable.Set // 可以在任何地方引入 可变集合

val mutableSet = Set(1,2,3)
println(mutableSet.getClass.getName) // scala.collection.mutable.HashSet

mutableSet.add(4)
mutableSet.remove(1)
mutableSet += 5
mutableSet -= 2

println(mutableSet) // Set(5, 3, 4)

val another = mutableSet.toSet
println(another.getClass.getName) // scala.collection.immutable.Set
1
2
3
4
5
6
7
8
9
10
11
12
13
14

注意: 虽然可变Set和不可变Set都有添加或删除元素的操作,但是有一个非常大的差别。对不可变Set进行操作,会产生一个新的set,原来的set并没有改变,这与List一样。 而对可变Set进行操作,改变的是该Set本身,与ListBuffer类似。


# 集合基本操作

Scala集合有三个基本操作:

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

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

object Test {
   def main(args: Array[String]) {
      val site = Set("Runoob", "Google", "Baidu")
      val nums: Set[Int] = Set()

      println( "第一网站是 : " + site.head )
      println( "最后一个网站是 : " + site.tail )
      println( "查看列表 site 是否为空 : " + site.isEmpty )
      println( "查看 nums 是否为空 : " + nums.isEmpty )
   }
}
1
2
3
4
5
6
7
8
9
10
11

执行以上代码,输出结果为:

$ vim Test.scala 
$ scala Test.scala 
第一网站是 : Runoob
最后一个网站是 : Set(Google, Baidu)
查看列表 site 是否为空 : false
查看 nums 是否为空 : true
1
2
3
4
5
6

# 连接集合

你可以使用 ++ 运算符或 Set.++() 方法来连接两个集合。如果元素有重复的就会移除重复的元素。实例如下:

object Test {
   def main(args: Array[String]) {
      val site1 = Set("Runoob", "Google", "Baidu")
      val site2 = Set("Faceboook", "Taobao")

      // ++ 作为运算符使用
      var site = site1 ++ site2
      println( "site1 ++ site2 : " + site )

      //  ++ 作为方法使用
      site = site1.++(site2)
      println( "site1.++(site2) : " + site )
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

执行以上代码,输出结果为:

$ vim Test.scala 
$ scala Test.scala 
site1 ++ site2 : Set(Faceboook, Taobao, Google, Baidu, Runoob)
site1.++(site2) : Set(Faceboook, Taobao, Google, Baidu, Runoob)
1
2
3
4

# 查找集合中最大与最小元素

你可以使用 Set.min 方法来查找集合中的最小元素,使用 Set.max 方法查找集合中的最大元素。实例如下:

object Test {
   def main(args: Array[String]) {
      val num = Set(5,6,9,20,30,45)

      // 查找集合中最大与最小元素
      println( "Set(5,6,9,20,30,45) 集合中的最小元素是 : " + num.min )
      println( "Set(5,6,9,20,30,45) 集合中的最大元素是 : " + num.max )
   }
}
1
2
3
4
5
6
7
8
9

# 交集

你可以使用 Set.& 方法或 Set.intersect 方法来查看两个集合的交集元素。实例如下:

object Test {
   def main(args: Array[String]) {
      val num1 = Set(5,6,9,20,30,45)
      val num2 = Set(50,60,9,20,35,55)

      // 交集
      println( "num1.&(num2) : " + num1.&(num2) )
      println( "num1.intersect(num2) : " + num1.intersect(num2) )
   }
}
1
2
3
4
5
6
7
8
9
10

执行以上代码,输出结果为:

$ vim Test.scala 
$ scala Test.scala 
num1.&(num2) : Set(20, 9)
num1.intersect(num2) : Set(20, 9)
1
2
3
4

# Scala Map

Map(映射)是一种可迭代的键值对(key/value)结构。

所有的值都可以通过键来获取。

Map 中的键都是唯一的。

Map 也叫哈希表(Hash tables)。

Map 有两种类型,可变与不可变,区别在于可变对象可以修改它,而不可变对象不可以。

默认情况下 Scala 使用不可变 Map。如果你需要使用可变集合,你需要显式的引入 import scala.collection.mutable.Map

在 Scala 中 你可以同时使用可变与不可变 Map,不可变的直接使用 Map,可变的使用 mutable.Map。以下实例演示了不可变 Map 的应用:

// 空哈希表,键为字符串,值为整型
var A:Map[Char,Int] = Map()

// Map 键值对演示
val colors = Map("red" -> "#FF0000", "azure" -> "#F0FFFF")
1
2
3
4
5

定义 Map 时,需要为键值对定义类型。如果需要添加 key-value 对,可以使用 + 号,如下所示:

A += ('I' -> 1)
A += ('J' -> 5)
A += ('K' -> 10)
A += ('L' -> 100)
1
2
3
4

# Map 基本操作

Scala Map 有三个基本操作:

方法 描述
keys 返回 Map 所有的键(key)
values 返回 Map 所有的值(value)
isEmpty 在 Map 为空时返回true

# 实例

以下实例演示了以上三个方法的基本应用:

object Test {
   def main(args: Array[String]) {
      val colors = Map("red" -> "#FF0000",
                       "azure" -> "#F0FFFF",
                       "peru" -> "#CD853F")

      val nums: Map[Int, Int] = Map()

      println( "colors 中的键为 : " + colors.keys )
      println( "colors 中的值为 : " + colors.values )
      println( "检测 colors 是否为空 : " + colors.isEmpty )
      println( "检测 nums 是否为空 : " + nums.isEmpty )
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
colors 中的键为 : Set(red, azure, peru)
colors 中的值为 : MapLike(#FF0000, #F0FFFF, #CD853F)
检测 colors 是否为空 : false
检测 nums 是否为空 : true
1
2
3
4
5
6

# Map 合并

你可以使用 ++ 运算符或 Map.++() 方法来连接两个 Map,Map 合并时会移除重复的 key。以下演示了两个 Map 合并的实例:

object Test {
   def main(args: Array[String]) {
      val colors1 = Map("red" -> "#FF0000",
                        "azure" -> "#F0FFFF",
                        "peru" -> "#CD853F")
      val colors2 = Map("blue" -> "#0033FF",
                        "yellow" -> "#FFFF00",
                        "red" -> "#FF0000")

      //  ++ 作为运算符
      var colors = colors1 ++ colors2
      println( "colors1 ++ colors2 : " + colors )

      //  ++ 作为方法
      colors = colors1.++(colors2)
      println( "colors1.++(colors2) : " + colors )

   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
colors1 ++ colors2 : Map(blue -> #0033FF, azure -> #F0FFFF, peru -> #CD853F, yellow -> #FFFF00, red -> #FF0000)
colors1.++(colors2) : Map(blue -> #0033FF, azure -> #F0FFFF, peru -> #CD853F, yellow -> #FFFF00, red -> #FF0000)
1
2
3
4

# 输出 Map 的 keys 和 values

以下通过 foreach 循环输出 Map 中的 keys 和 values:

object Test {
   def main(args: Array[String]) {
      val sites = Map("runoob" -> "http://www.runoob.com",
                       "baidu" -> "http://www.baidu.com",
                       "taobao" -> "http://www.taobao.com")

      sites.keys.foreach{ i =>  
                           print( "Key = " + i )
                           println(" Value = " + sites(i) )}
   }
}
1
2
3
4
5
6
7
8
9
10
11

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
Key = runoob Value = http://www.runoob.com
Key = baidu Value = http://www.baidu.com
Key = taobao Value = http://www.taobao.com
1
2
3
4
5

# 查看 Map 中是否存在指定的 Key

你可以使用 Map.contains 方法来查看 Map 中是否存在指定的 Key。实例如下:

object Test {
   def main(args: Array[String]) {
      val sites = Map("runoob" -> "http://www.runoob.com",
                       "baidu" -> "http://www.baidu.com",
                       "taobao" -> "http://www.taobao.com")

      if( sites.contains( "runoob" )){
           println("runoob 键存在,对应的值为 :"  + sites("runoob"))
      }else{
           println("runoob 键不存在")
      }
      if( sites.contains( "baidu" )){
           println("baidu 键存在,对应的值为 :"  + sites("baidu"))
      }else{
           println("baidu 键不存在")
      }
      if( sites.contains( "google" )){
           println("google 键存在,对应的值为 :"  + sites("google"))
      }else{
           println("google 键不存在")
      }
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
runoob 键存在,对应的值为 :http://www.runoob.com
baidu 键存在,对应的值为 :http://www.baidu.com
google 键不存在
1
2
3
4
5

# Scala 元组

与列表一样,元组也是不可变的,但与列表不同的是元组可以包含不同类型的元素。

元组的值是通过将单个的值包含在圆括号中构成的。例如:

val t = (1, 3.14, "Fred")  
1

以上实例在元组中定义了三个元素,对应的类型分别为[Int, Double, java.lang.String]。

此外我们也可以使用以下方式来定义:

val t = new Tuple3(1, 3.14, "Fred")
1

元组的实际类型取决于它的元素的类型,比如 (99, "runoob") 是 Tuple2[Int, String]。 ('u', 'r', "the", 1, 4, "me") 为 Tuple6[Char, Char, String, Int, Int, String]。

目前 Scala 支持的元组最大长度为 22。对于更大长度你可以使用集合,或者扩展元组。

访问元组的元素可以通过数字索引,如下一个元组:

val t = (4,3,2,1)
1

我们可以使用 t._1 访问第一个元素, t._2 访问第二个元素,如下所示:

object Test {
   def main(args: Array[String]) {
      val t = (4,3,2,1)

      val sum = t._1 + t._2 + t._3 + t._4

      println( "元素之和为: "  + sum )
   }
}
1
2
3
4
5
6
7
8
9

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
元素之和为: 10
1
2
3

# 迭代元组

你可以使用 Tuple.productIterator() 方法来迭代输出元组的所有元素:

object Test {
   def main(args: Array[String]) {
      val t = (4,3,2,1)
      
      t.productIterator.foreach{ i =>println("Value = " + i )}
   }
}
1
2
3
4
5
6
7

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
Value = 4
Value = 3
Value = 2
Value = 1
1
2
3
4
5
6

# 元组转为字符串

你可以使用 Tuple.toString() 方法将元组的所有元素组合成一个字符串,实例如下:

object Test {
   def main(args: Array[String]) {
      val t = new Tuple3(1, "hello", Console)
      
      println("连接后的字符串为: " + t.toString() )
   }
}
1
2
3
4
5
6
7

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
连接后的字符串为: (1,hello,scala.Console$@4dd8dc3)
1
2
3

# 元素交换

你可以使用 Tuple.swap 方法来交换元组的元素。如下实例:

object Test {
   def main(args: Array[String]) {
      val t = new Tuple2("www.google.com", "www.runoob.com")
      
      println("交换后的元组: " + t.swap )
   }
}
1
2
3
4
5
6
7

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
交换后的元组: (www.runoob.com,www.google.com)
1
2
3

# Scala Option

Scala Option(选项)类型用来表示一个值是可选的(有值或无值)。

Option[T] 是一个类型为 T 的可选值的容器: 如果值存在, Option[T] 就是一个 Some[T] ,如果不存在, Option[T] 就是对象 None 。

接下来我们来看一段代码:

// 虽然 Scala 可以不定义变量的类型,不过为了清楚些,我还是
// 把他显示的定义上了
 
val myMap: Map[String, String] = Map("key1" -> "value")
val value1: Option[String] = myMap.get("key1")
val value2: Option[String] = myMap.get("key2")
 
println(value1) // Some("value1")
println(value2) // None
1
2
3
4
5
6
7
8
9

在上面的代码中,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,则代表没有字符串可以给你。

另一个实例:

object Test {
   def main(args: Array[String]) {
      val sites = Map("runoob" -> "www.runoob.com", "google" -> "www.google.com")
      
      println("sites.get( \"runoob\" ) : " +  sites.get( "runoob" )) // Some(www.runoob.com)
      println("sites.get( \"baidu\" ) : " +  sites.get( "baidu" ))  //  None
   }
}
1
2
3
4
5
6
7
8

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
sites.get( "runoob" ) : Some(www.runoob.com)
sites.get( "baidu" ) : None
1
2
3
4

你也可以通过模式匹配来输出匹配值。实例如下:

object Test {
   def main(args: Array[String]) {
      val sites = Map("runoob" -> "www.runoob.com", "google" -> "www.google.com")
      
      println("show(sites.get( \"runoob\")) : " +  
                                          show(sites.get( "runoob")) )
      println("show(sites.get( \"baidu\")) : " +  
                                          show(sites.get( "baidu")) )
   }
   
   def show(x: Option[String]) = x match {
      case Some(s) => s
      case None => "?"
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
show(sites.get( "runoob")) : www.runoob.com
show(sites.get( "baidu")) : ?
1
2
3
4

# getOrElse() 方法

你可以使用 getOrElse() 方法来获取元组中存在的元素或者使用其默认的值,实例如下:

object Test {
   def main(args: Array[String]) {
      val a:Option[Int] = Some(5)
      val b:Option[Int] = None 
      
      println("a.getOrElse(0): " + a.getOrElse(0) )
      println("b.getOrElse(10): " + b.getOrElse(10) )
   }
}
1
2
3
4
5
6
7
8
9

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
a.getOrElse(0): 5
b.getOrElse(10): 10
1
2
3
4

# isEmpty() 方法

你可以使用 isEmpty() 方法来检测元组中的元素是否为 None,实例如下:

object Test {
   def main(args: Array[String]) {
      val a:Option[Int] = Some(5)
      val b:Option[Int] = None 
      
      println("a.isEmpty: " + a.isEmpty )
      println("b.isEmpty: " + b.isEmpty )
   }
}
1
2
3
4
5
6
7
8
9

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
a.isEmpty: false
b.isEmpty: true
1
2
3
4

# Scala Iterator

Scala Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法。

迭代器 it 的两个基本操作是 nexthasNext

调用 it.next() 会返回迭代器的下一个元素,并且更新迭代器的状态。

调用 it.hasNext() 用于检测集合中是否还有元素。

让迭代器 it 逐个返回所有元素最简单的方法是使用 while 循环:

object Test {
   def main(args: Array[String]) {
      val it = Iterator("Baidu", "Google", "Runoob", "Taobao")
      
      while (it.hasNext){
         println(it.next())
      }
   }
}
1
2
3
4
5
6
7
8
9

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
Baidu
Google
Runoob
Taobao
1
2
3
4
5
6

# 查找最大与最小元素

你可以使用 it.minit.max 方法从迭代器中查找最大与最小元素,实例如下:

object Test {
   def main(args: Array[String]) {
      val ita = Iterator(20,40,2,50,69, 90)
      val itb = Iterator(20,40,2,50,69, 90)
      
      println("最大元素是:" + ita.max )
      println("最小元素是:" + itb.min )

   }
}
1
2
3
4
5
6
7
8
9
10

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
最大元素是:90
最小元素是:2
1
2
3
4

# 获取迭代器的长度

你可以使用 it.sizeit.length 方法来查看迭代器中的元素个数。实例如下:

object Test {
   def main(args: Array[String]) {
      val ita = Iterator(20,40,2,50,69, 90)
      val itb = Iterator(20,40,2,50,69, 90)
      
      println("ita.size 的值: " + ita.size )
      println("itb.length 的值: " + itb.length )

   }
}
1
2
3
4
5
6
7
8
9
10

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
ita.size 的值: 6
itb.length 的值: 6
1
2
3
4

# 集合实例

以下代码判断,演示了所有以上集合类型的定义实例:

// 定义整型 List
val x = List(1,2,3,4)

// 定义 Set
val x = Set(1,3,5,7)

// 定义 Map
val x = Map("one" -> 1, "two" -> 2, "three" -> 3)

// 创建两个不同类型元素的元组
val x = (10, "Runoob")

// 定义 Option
val x:Option[Int] = Some(5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# Scala类和对象

类是对象的抽象,而对象是类的具体实例。类是抽象的,不占用内存,而对象是具体的,占用存储空间。类是用于创建对象的蓝图,它是一个定义包括在特定类型的对象中的方法和变量的软件模板。

我们可以使用 new 关键字来创建类的对象,实例如下:

class Point(xc: Int, yc: Int) {
   var x: Int = xc
   var y: Int = yc

   def move(dx: Int, dy: Int) {
      x = x + dx
      y = y + dy
      println ("x 的坐标点: " + x);
      println ("y 的坐标点: " + y);
   }
}
1
2
3
4
5
6
7
8
9
10
11

Scala中的类不声明为public,一个Scala源文件中可以有多个类。

以上实例的类定义了两个变量 xy ,一个方法:move,方法没有返回值。

Scala 的类定义可以有参数,称为类参数,如上面的 xc, yc,类参数在整个类中都可以访问。

接着我们可以使用 new 来实例化类,并访问类中的方法和变量:

import java.io._

class Point(xc: Int, yc: Int) {
   var x: Int = xc
   var y: Int = yc

   def move(dx: Int, dy: Int) {
      x = x + dx
      y = y + dy
      println ("x 的坐标点: " + x);
      println ("y 的坐标点: " + y);
   }
}

object Test {
   def main(args: Array[String]) {
      val pt = new Point(10, 20);

      // 移到一个新的位置
      pt.move(10, 10);
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
x 的坐标点: 20
y 的坐标点: 30
1
2
3
4

# Scala 继承

Scala继承一个基类跟Java很相似, 但我们需要注意以下几点:

  • 1、重写一个非抽象方法必须使用override修饰符。
  • 2、只有主构造函数才可以往基类的构造函数里写参数。
  • 3、在子类中重写超类的抽象方法时,你不需要使用override关键字。

接下来让我们来看个实例:

class Point(xc: Int, yc: Int) {
   var x: Int = xc
   var y: Int = yc

   def move(dx: Int, dy: Int) {
      x = x + dx
      y = y + dy
      println ("x 的坐标点: " + x);
      println ("y 的坐标点: " + y);
   }
}

class Location(override val xc: Int, override val yc: Int,
   val zc :Int) extends Point(xc, yc){
   var z: Int = zc

   def move(dx: Int, dy: Int, dz: Int) {
      x = x + dx
      y = y + dy
      z = z + dz
      println ("x 的坐标点 : " + x);
      println ("y 的坐标点 : " + y);
      println ("z 的坐标点 : " + z);
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

Scala 使用 extends 关键字来继承一个类。实例中 Location 类继承了 Point 类。Point 称为父类(基类),Location 称为子类。

override val xc 为重写了父类的字段。

继承会继承父类的所有属性和方法,Scala 只允许继承一个父类。

实例如下:

import java.io._

class Point(val xc: Int, val yc: Int) {
   var x: Int = xc
   var y: Int = yc
   def move(dx: Int, dy: Int) {
      x = x + dx
      y = y + dy
      println ("x 的坐标点 : " + x);
      println ("y 的坐标点 : " + y);
   }
}

class Location(override val xc: Int, override val yc: Int,
   val zc :Int) extends Point(xc, yc){
   var z: Int = zc

   def move(dx: Int, dy: Int, dz: Int) {
      x = x + dx
      y = y + dy
      z = z + dz
      println ("x 的坐标点 : " + x);
      println ("y 的坐标点 : " + y);
      println ("z 的坐标点 : " + z);
   }
}

object Test {
   def main(args: Array[String]) {
      val loc = new Location(10, 20, 15);

      // 移到一个新的位置
      loc.move(10, 10, 5);
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
x 的坐标点 : 20
y 的坐标点 : 30
z 的坐标点 : 20
1
2
3
4
5

Scala重写一个非抽象方法,必须用override修饰符。

class Person {
  var name = ""
  override def toString = getClass.getName + "[name=" + name + "]"
}

class Employee extends Person {
  var salary = 0.0
  override def toString = super.toString + "[salary=" + salary + "]"
}

object Test extends App {
  val fred = new Employee
  fred.name = "Fred"
  fred.salary = 50000
  println(fred)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
Employee[name=Fred][salary=50000.0]
1
2
3

# Scala 单例对象

在 Scala 中,是没有 static 这个东西的,但是它也为我们提供了单例模式的实现方法,那就是使用关键字 object。

Scala 中使用单例模式时,除了定义的类之外,还要定义一个同名的 object 对象,它和类的区别是,object对象不能带参数。

当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象:companion object。你必须在同一个源文件里定义类和它的伴生对象。类被称为是这个单例对象的伴生类:companion class。类和它的伴生对象可以互相访问其私有成员。

# 单例对象实例

import java.io._

class Point(val xc: Int, val yc: Int) {
   var x: Int = xc
   var y: Int = yc
   def move(dx: Int, dy: Int) {
      x = x + dx
      y = y + dy
   }
}

object Test {
   def main(args: Array[String]) {
      val point = new Point(10, 20)
      printPoint

      def printPoint{
         println ("x 的坐标点 : " + point.x);
         println ("y 的坐标点 : " + point.y);
      }
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
x 的坐标点 : 10
y 的坐标点 : 20
1
2
3
4

# 伴生对象实例

/* 文件名:Marker.scala
 */

// 私有构造方法
class Marker private(val color:String) {

  println("创建" + this)
  
  override def toString(): String = "颜色标记:"+ color
  
}

// 伴生对象,与类名字相同,可以访问类的私有属性和方法
object Marker{
  
    private val markers: Map[String, Marker] = Map(
      "red" -> new Marker("red"),
      "blue" -> new Marker("blue"),
      "green" -> new Marker("green")
    )
    
    def apply(color:String) = {
      if(markers.contains(color)) markers(color) else null
    }
  
    
    def getMarker(color:String) = { 
      if(markers.contains(color)) markers(color) else null
    }
    def main(args: Array[String]) { 
        println(Marker("red"))  
        // 单例函数调用,省略了.(点)符号  
        println(Marker getMarker "blue")  
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

执行以上代码,输出结果为:

$ scalac Marker.scala 
$ scala Marker
创建颜色标记:red
创建颜色标记:blue
创建颜色标记:green
颜色标记:red
颜色标记:blue
1
2
3
4
5
6
7

# Scala Trait(特征)

Scala Trait(特征) 相当于 Java 的接口,实际上它比接口还功能强大。

与接口不同的是,它还可以定义属性和方法的实现。

一般情况下Scala的类只能够继承单一父类,但是如果是 Trait(特征) 的话就可以继承多个,从结果来看就是实现了多重继承。

Trait(特征) 定义的方式与类类似,但它使用的关键字是 trait,如下所示:

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

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

以下演示了特征的完整实例:

/* 文件名:Test.scala
 */
trait Equal {
  def isEqual(x: Any): Boolean
  def isNotEqual(x: Any): Boolean = !isEqual(x)
}

class Point(xc: Int, yc: Int) extends Equal {
  var x: Int = xc
  var y: Int = yc
  def isEqual(obj: Any) =
    obj.isInstanceOf[Point] &&
    obj.asInstanceOf[Point].x == x
}

object Test {
   def main(args: Array[String]) {
      val p1 = new Point(2, 3)
      val p2 = new Point(2, 4)
      val p3 = new Point(3, 3)

      println(p1.isNotEqual(p2))
      println(p1.isNotEqual(p3))
      println(p1.isNotEqual(2))
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
false
true
true
1
2
3
4
5

# 特征构造顺序

特征也可以有构造器,由字段的初始化和其他特征体中的语句构成。这些语句在任何混入该特征的对象在构造时都会被执行。

构造器的执行顺序:

  • 调用超类的构造器;
  • 特征构造器在超类构造器之后、类构造器之前执行;
  • 特征由左到右被构造;
  • 每个特征当中,父特征先被构造;
  • 如果多个特征共有一个父特征,父特征不会被重复构造
  • 所有特征被构造完毕,子类被构造。

构造器的顺序是类的线性化的反向。线性化是描述某个类型的所有超类型的一种技术规格。

# Scala模式匹配

Scala 提供了强大的模式匹配机制,应用也非常广泛。

一个模式匹配包含了一系列备选项,每个都开始于关键字 case。每个备选项都包含了一个模式及一到多个表达式。箭头符号 => 隔开了模式和表达式。

以下是一个简单的整型值模式匹配实例:

object Test {
   def main(args: Array[String]) {
      println(matchTest(3))

   }
   def matchTest(x: Int): String = x match {
      case 1 => "one"
      case 2 => "two"
      case _ => "many"
   }
}
1
2
3
4
5
6
7
8
9
10
11

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
many
1
2
3

match 对应 Java 里的 switch,但是写在选择器表达式之后。即: 选择器 match {备选项}。

match 表达式通过以代码编写的先后次序尝试每个模式来完成计算,只要发现有一个匹配的case,剩下的case不会继续匹配。

接下来我们来看一个不同数据类型的模式匹配:

object Test {
   def main(args: Array[String]) {
      println(matchTest("two"))
      println(matchTest("test"))
      println(matchTest(1))
      println(matchTest(6))

   }
   def matchTest(x: Any): Any = x match {
      case 1 => "one"
      case "two" => 2
      case y: Int => "scala.Int"
      case _ => "many"
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
2
many
one
scala.Int
1
2
3
4
5
6

实例中第一个 case 对应整型数值 1,第二个 case 对应字符串值 two,第三个 case 对应类型模式,用于判断传入的值是否为整型,相比使用isInstanceOf来判断类型,使用模式匹配更好。第四个 case 表示默认的全匹配备选项,即没有找到其他匹配时的匹配项,类似 switch 中的 default。


# 使用样例类

使用了case关键字的类定义就是就是样例类(case classes),样例类是种特殊的类,经过优化以用于模式匹配。

以下是样例类的简单实例:

object Test {
   def main(args: Array[String]) {
       val alice = new Person("Alice", 25)
    val bob = new Person("Bob", 32)
       val charlie = new Person("Charlie", 32)
   
    for (person <- List(alice, bob, charlie)) {
        person match {
            case Person("Alice", 25) => println("Hi Alice!")
            case Person("Bob", 32) => println("Hi Bob!")
            case Person(name, age) =>
               println("Age: " + age + " year, name: " + name + "?")
         }
      }
   }
   // 样例类
   case class Person(name: String, age: Int)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
Hi Alice!
Hi Bob!
Age: 32 year, name: Charlie?
1
2
3
4
5

在声明样例类时,下面的过程自动发生了:

  • 构造器的每个参数都成为val,除非显式被声明为var,但是并不推荐这么做;
  • 在伴生对象中提供了apply方法,所以可以不使用new关键字就可构建对象;
  • 提供unapply方法使模式匹配可以工作;
  • 生成toString、equals、hashCode和copy方法,除非显示给出这些方法的定义。

# Scala正则表达式

Scala 通过 scala.util.matching 包中的 Regex 类来支持正则表达式。以下实例演示了使用正则表达式查找单词 Scala :

import scala.util.matching.Regex

object Test {
   def main(args: Array[String]) {
      val pattern = "Scala".r
      val str = "Scala is Scalable and cool"
      
      println(pattern findFirstIn str)
   }
}
1
2
3
4
5
6
7
8
9
10

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
Some(Scala)
1
2
3

实例中使用 String 类的 r() 方法构造了一个Regex对象。

然后使用 findFirstIn 方法找到首个匹配项。

如果需要查看所有的匹配项可以使用 findAllIn 方法。

你可以使用 mkString( ) 方法来连接正则表达式匹配结果的字符串,并可以使用管道(|)来设置不同的模式:

import scala.util.matching.Regex

object Test {
   def main(args: Array[String]) {
      val pattern = new Regex("(S|s)cala")  // 首字母可以是大写 S 或小写 s
      val str = "Scala is scalable and cool"
      
      println((pattern findAllIn str).mkString(","))   // 使用逗号 , 连接返回结果
   }
}
1
2
3
4
5
6
7
8
9
10

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
Scala,scala
1
2
3

如果你需要将匹配的文本替换为指定的关键词,可以使用 replaceFirstIn( ) 方法来替换第一个匹配项,使用 replaceAllIn( ) 方法替换所有匹配项,实例如下:

object Test {
   def main(args: Array[String]) {
      val pattern = "(S|s)cala".r
      val str = "Scala is scalable and cool"
      
      println(pattern replaceFirstIn(str, "Java"))
   }
}
1
2
3
4
5
6
7
8

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
Java is scalable and cool
1
2
3

# 正则表达式

Scala 的正则表达式继承了 Java 的语法规则,Java 则大部分使用了 Perl 语言的规则。

下表我们给出了常用的一些正则表达式规则:

表达式 匹配规则
^ 匹配输入字符串开始的位置。
$ 匹配输入字符串结尾的位置。
. 匹配除"\r\n"之外的任何单个字符。
[...] 字符集。匹配包含的任一字符。例如,"[abc]"匹配"plain"中的"a"。
[^...] 反向字符集。匹配未包含的任何字符。例如,"[^abc]"匹配"plain"中"p","l","i","n"。
\A 匹配输入字符串开始的位置(无多行支持)
\z 字符串结尾(类似$,但不受处理多行选项的影响)
\Z 字符串结尾或行尾(不受处理多行选项的影响)
re* 重复零次或更多次
re+ 重复一次或更多次
re? 重复零次或一次
re{ n} 重复n次
re{ n,}
re{ n, m} 重复n到m次
a|b 匹配 a 或者 b
(re) 匹配 re,并捕获文本到自动命名的组里
(?: re) 匹配 re,不捕获匹配的文本,也不给此分组分配组号
(?> re) 贪婪子表达式
\w 匹配字母或数字或下划线或汉字
\W 匹配任意不是字母,数字,下划线,汉字的字符
\s 匹配任意的空白符,相等于 [\t\n\r\f]
\S 匹配任意不是空白符的字符
\d 匹配数字,类似 [0-9]
\D 匹配任意非数字的字符
\G 当前搜索的开头
\n 换行符
\b 通常是单词分界位置,但如果在字符类里使用代表退格
\B 匹配不是单词开头或结束的位置
\t 制表符
\Q 开始引号:\Q(a+b)*3\E 可匹配文本 "(a+b)*3"。
\E 结束引号:\Q(a+b)*3\E 可匹配文本 "(a+b)*3"。

# 正则表达式实例

实例 描述
. 匹配除"\r\n"之外的任何单个字符。
[Rr]uby 匹配 "Ruby" 或 "ruby"
rub[ye] 匹配 "ruby" 或 "rube"
[aeiou] 匹配小写字母 :aeiou
[0-9] 匹配任何数字,类似 [0123456789]
[a-z] 匹配任何 ASCII 小写字母
[A-Z] 匹配任何 ASCII 大写字母
[a-zA-Z0-9] 匹配数字,大小写字母
[^aeiou] 匹配除了 aeiou 其他字符
[^0-9] 匹配除了数字的其他字符
\d 匹配数字,类似: [0-9]
\D 匹配非数字,类似: [^0-9]
\s 匹配空格,类似: [ \t\r\n\f]
\S 匹配非空格,类似: [^ \t\r\n\f]
\w 匹配字母,数字,下划线,类似: [A-Za-z0-9_]
\W 匹配非字母,数字,下划线,类似: [^A-Za-z0-9_]
ruby? 匹配 "rub" 或 "ruby": y 是可选的
ruby* 匹配 "rub" 加上 0 个或多个的 y。
ruby+ 匹配 "rub" 加上 1 个或多个的 y。
\d{3} 刚好匹配 3 个数字。
\d{3,} 匹配 3 个或多个数字。
\d{3,5} 匹配 3 个、4 个或 5 个数字。
\D\d+ 无分组: + 重复 \d
(\D\d)+/ 分组: + 重复 \D\d 对
([Rr]uby(, )?)+ 匹配 "Ruby"、"Ruby, ruby, ruby",等等

注意上表中的每个字符使用了两个反斜线。这是因为在 Java 和 Scala 中字符串中的反斜线是转义字符。所以如果你要输出 ..,你需要在字符串中写成 .\. 来获取一个反斜线。查看以下实例:

import scala.util.matching.Regex

object Test {
   def main(args: Array[String]) {
      val pattern = new Regex("abl[ae]\\d+")
      val str = "ablaw is able1 and cool"
      
      println((pattern findAllIn str).mkString(","))
   }
}
1
2
3
4
5
6
7
8
9
10

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
able1
1
2
3

# Scala异常处理

Scala 的异常处理和其它语言比如 Java 类似。

Scala 的方法可以通过抛出异常的方法的方式来终止相关代码的运行,不必通过返回值。


# 抛出异常

Scala 抛出异常的方法和 Java一样,使用 throw 方法,例如,抛出一个新的参数异常:

throw new IllegalArgumentException
1

# 捕获异常

异常捕捉的机制与其他语言中一样,如果有异常发生,catch字句是按次序捕捉的。因此,在catch字句中,越具体的异常越要靠前,越普遍的异常越靠后。 如果抛出的异常不在catch字句中,该异常则无法处理,会被升级到调用者处。

捕捉异常的catch子句,语法与其他语言中不太一样。在Scala里,借用了模式匹配的思想来做异常的匹配,因此,在catch的代码里,是一系列case字句,如下例所示:

import java.io.FileReader
import java.io.FileNotFoundException
import java.io.IOException

object Test {
   def main(args: Array[String]) {
      try {
         val f = new FileReader("input.txt")
      } catch {
         case ex: FileNotFoundException =>{
            println("Missing file exception")
         }
         case ex: IOException => {
            println("IO Exception")
         }
      }
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
Missing file exception
1
2
3

catch字句里的内容跟match里的case是完全一样的。由于异常捕捉是按次序,如果最普遍的异常,Throwable,写在最前面,则在它后面的case都捕捉不到,因此需要将它写在最后面。


# finally 语句

finally 语句用于执行不管是正常处理还是有异常发生时都需要执行的步骤,实例如下:

import java.io.FileReader
import java.io.FileNotFoundException
import java.io.IOException

object Test {
   def main(args: Array[String]) {
      try {
         val f = new FileReader("input.txt")
      } catch {
         case ex: FileNotFoundException => {
            println("Missing file exception")
         }
         case ex: IOException => {
            println("IO Exception")
         }
      } finally {
         println("Exiting finally...")
      }
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
Missing file exception
Exiting finally...
1
2
3
4

# Scala提取器(Extractor)

提取器是从传递给它的对象中提取出构造该对象的参数。

Scala 标准库包含了一些预定义的提取器,我们会大致的了解一下它们。

Scala 提取器是一个带有unapply方法的对象。unapply方法算是apply方法的反向操作:unapply接受一个对象,然后从对象中提取值,提取的值通常是用来构造该对象的值。

以下实例演示了邮件地址的提取器对象:

object Test {
   def main(args: Array[String]) {
      
      println ("Apply 方法 : " + apply("Zara", "gmail.com"));
      println ("Unapply 方法 : " + unapply("Zara@gmail.com"));
      println ("Unapply 方法 : " + unapply("Zara Ali"));

   }
   // 注入方法 (可选)
   def apply(user: String, domain: String) = {
      user +"@"+ domain
   }

   // 提取方法(必选)
   def unapply(str: String): Option[(String, String)] = {
      val parts = str split "@"
      if (parts.length == 2){
         Some(parts(0), parts(1)) 
      }else{
         None
      }
   }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
Apply 方法 : Zara@gmail.com
Unapply 方法 : Some((Zara,gmail.com))
Unapply 方法 : None
1
2
3
4
5

以上对象定义了两个方法: applyunapply 方法。通过 apply 方法我们无需使用 new 操作就可以创建对象。所以你可以通过语句 Test("Zara", "gmail.com") 来构造一个字符串 "Zara@gmail.com"。

unapply方法算是apply方法的反向操作:unapply接受一个对象,然后从对象中提取值,提取的值通常是用来构造该对象的值。实例中我们使用 Unapply 方法从对象中提取用户名和邮件地址的后缀。

实例中 unapply 方法在传入的字符串不是邮箱地址时返回 None。代码演示如下:

unapply("Zara@gmail.com") 相等于 Some("Zara", "gmail.com")
unapply("Zara Ali") 相等于 None
1
2

# 提取器使用模式匹配

在我们实例化一个类的时,可以带上0个或者多个的参数,编译器在实例化的时会调用 apply 方法。我们可以在类和对象中都定义 apply 方法。

就像我们之前提到过的,unapply 用于提取我们指定查找的值,它与 apply 的操作相反。 当我们在提取器对象中使用 match 语句是,unapply 将自动执行,如下所示:

object Test {
   def main(args: Array[String]) {
      
      val x = Test(5)
      println(x)

      x match
      {
         case Test(num) => println(x + " 是 " + num + " 的两倍!")
         //unapply 被调用
         case _ => println("无法计算")
      }

   }
   def apply(x: Int) = x*2
   def unapply(z: Int): Option[Int] = if (z%2==0) Some(z/2) else None
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
10
10 是 5 的两倍!
1
2
3
4

# Scala 文件I/O

# Scala 进行文件写操作,直接用的都是 java中 的 I/O 类 (java.io.File):

import java.io._

object Test {
   def main(args: Array[String]) {
      val writer = new PrintWriter(new File("test.txt" ))

      writer.write("菜鸟教程")
      writer.close()
   }
}
1
2
3
4
5
6
7
8
9
10

执行以上代码,会在你的当前目录下生产一个 test.txt 文件,文件内容为"菜鸟教程":

$ scalac Test.scala 
$ scala Test
$ cat test.txt 
菜鸟教程
1
2
3
4

# 从屏幕上读取用户输入

有时候我们需要接收用户在屏幕输入的指令来处理程序。实例如下:

import scala.io._
object Test {
   def main(args: Array[String]) {
      print("请输入weekdawn : " )
      val line = StdIn.readLine()

      println("谢谢,你输入的是: " + line)
   }
}
1
2
3
4
5
6
7
8
9

Scala2.11 后的版本 Console.readLine 已废弃,使用 scala.io.StdIn.readLine() 方法代替。

执行以上代码,屏幕上会显示如下信息:

$ scalac Test.scala 
$ scala Test
请输入菜鸟教程官网 : www.runoob.com
谢谢,你输入的是: www.runoob.com
1
2
3
4

# 从文件上读取内容

从文件读取内容非常简单。我们可以使用 Scala 的 Source 类及伴生对象来读取文件。以下实例演示了从 "test.txt"(之前已创建过) 文件中读取内容:

import scala.io.Source

object Test {
   def main(args: Array[String]) {
      println("文件内容为:" )

      Source.fromFile("test.txt" ).foreach{ 
         print 
      }
   }
}
1
2
3
4
5
6
7
8
9
10
11

执行以上代码,输出结果为:

$ scalac Test.scala 
$ scala Test
文件内容为:
weekdawn
1
2
3
4
上次更新: 2024/09/10 17:37:12
最近更新
01
跨域的几种常见解决方案
04-03
02
react教程
03-01
03
前端抓包神器whistle
09-01