一文学会Scala

总体介绍

image

  • Scala 是一门多范式(multi-paradigm)的编程语言,设计初衷是要集成面向对象编程和函数式编程的各类特性。
  • 联邦理工学院洛桑(EPFL)的Martin Odersky于2001年基于Funnel的工做开始设计Scala。Java平台的Scala于2003年末/2004年初发布。.NET平台的Scala发布于2004年6月。该语言第二个版本,v2.0,发布于2006年3月。
  • Scala 运行在Java虚拟机上,并兼容现有的Java程序。
  • Scala 源代码被编译成Java字节码,因此它能够运行于JVM之上,并能够调用现有的Java类库。

Scala 特性

面向对象特性

Scala能够是一种面向对象的语言,每一个值均可以是对象。javascript

函数式编程

Scala提供了轻量级的语法用以定义匿名函数,支持高阶函数,容许嵌套多层函数,并支持柯里化。php

静态类型

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

扩展性

Scala提供了许多独特的语言机制,能够以库的形式轻易无缝添加新的语言结构:html

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

并发性

Scala使用Actor做为其并发模型,Actor是相似线程的实体,经过邮箱发收消息。Actor能够复用线程,所以在程序中可使用数百万个Actor,而线程只能建立数千个。在2.10以后的版本中,使用Akka做为其默认Actor实现。java

Scala 包

定义包

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

第一种方法

和 Java 同样,在文件的头定义包名,这种方法就后续全部代码都放在该包中。 好比:nginx

package com.runoob class HelloWorld{ }

第二种方法

有些相似 C#,如:面试

package com.runoob { class HelloWorld{ } }

第二种方法,能够在一个文件中定义多个包。正则表达式

引用包

Scala 使用 import 关键字引用包。sql

import java.awt.Color // 引入Color import java.awt._ // 引入包内全部成员 def handler(evt: event.ActionEvent) { // java.awt.event.ActionEvent ... // 由于引入了java.awt,因此能够省去前面的部分 }

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

若是想要引入包中的几个成员,可使用selector语法糖(选取器):

import java.awt.{Color, Font} // 重命名成员 import java.util.{HashMap => JavaHashMap} // 隐藏成员 import java.util.{HashMap => _, _} // 引入了util包的全部成员,可是HashMap被隐藏了

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

数据类型

Scala的数据类型和Java差很少,如下是和Java不一样之处.

数据类型 描述
java... 和java同样,8种基本类型...
Unit 表示无值,和其余语言中void等同。用做不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()。
Null null 或空引用
Nothing Nothing类型在Scala的类层级的最低端;它是任何其余类型的子类型。
Any Any是全部其余类的超类
AnyRef AnyRef类是Scala里全部引用类(reference class)的基类

上表中列出的数据类型都是对象,也就是说scala没有java中的原生类型。在scala是能够对数字等基础类型调用方法的。

类型的层级关系

关于Scala的类型关系,能够看个人另外一篇文章Scala的类层级讲解

符号字面量

符号字面量 'x 是表达式 scala.Symbol("x") 的简写;

多行字符串的表示方法

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

Scala 变量

变量是一种使用方便的占位符,用于引用计算机内存地址,变量建立后会占用必定的内存空间。

变量声明

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

var myVar : String = "Foo" val myVal : String = "Foo"

在 Scala 中声明变量和常量不必定要指明数据类型,在没有指明数据类型的状况下,其数据类型是经过变量或常量的初始值推断出来的。
++因此,若是在没有指明数据类型的状况下声明变量或常量必需要给出其初始值,不然将会报错。++

Scala 支持多个变量同时的声明赋值:

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

访问修饰符

分别有:private,protected,public(默认)。

私有(Private)

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

class Outer{ class Inner{ private def f(){ println("f") } class InnerMost{ f() // 正确 } } (new Inner).f() //错误 }

保护(Protected)

对保护(Protected)成员的访问比 java 更严格一些。
由于它只容许保护成员在定义了该成员的的类的子类中被访问,同包不可访问。

package p{ class Super{ protected def f() { println("f") } } class Sub extends Super{ f() } class Other{ (new Super).f() //错误 } }

公共(Public)

若是没有指定任何的修饰符,则默认为 public。这样的成员在任何地方均可以被访问。

做用域保护

Scala中,访问修饰符能够经过使用限定词强调。格式为:

private[x] 或 protected[x]

这里的x指代某个所属的包、类或单例对象。若是写成private[x],读做"这个成员除了对[…]中的类或[…]中的包中的类及它们的伴生对象可见外,对其它全部类都是private。

这种技巧在横跨了若干包的大型项目中很是有用,它容许你定义一些在你项目的若干子包中可见但对于项目外部的客户却始终不可见的东西。

package bobsrocckets{ package navigation{ private[bobsrockets] class Navigator{ protected[navigation] def useStarChart(){} class LegOfJourney{ private[Navigator] val distance = 100 } private[this] var speed = 200 } } package launch{ import navigation._ object Vehicle{ private[launch] val guide = new Navigator } } }

上述例子中,类Navigator被标记为private[bobsrockets]就是说这个类对包含在bobsrockets包里的全部的类和对象可见。
好比说,从Vehicle对象里对Navigator的访问是被容许的,由于对象Vehicle包含在包launch中,而launch包在bobsrockets中,相反,全部在包bobsrockets以外的代码都不能访问类Navigator。

Scala 运算符

算术运算符

和java同样+ , - , * , / , %

关系运算符

和java同样 == , != , > , < , >= , <=

逻辑运算符

和java同样&& , || , !

位运算符

和java同样& , | , ^ , ~ , << , >> , >>>
~ 按位取反

无符号右移

赋值运算符

和java同样

流程语句

if判断语句

和java同样,支持嵌套同时有返回值.

循环语句

和java彻底同样,支持嵌套.

Scala 函数

Scala 有函数和方法,两者在语义上的区别很小。

Scala 方法是类的一部分,而函数是一个对象能够赋值给一个变量。换句话来讲在类中定义的函数便是方法。

咱们能够在任何地方定义函数,甚至能够在函数内定义函数(内嵌函数)。更重要的一点是 Scala 函数名能够有如下特殊字符:+, ++, ~, &,-, -- , \, /, : 等。

函数声明

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

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

函数定义

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

以下,一个求加法的函数:

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

若是函数没有返回值,能够返回为 Unit,这个相似于 Java 的 void.

Scala的灵活性,使的函数定义的方法多种多样:
一、规规矩矩的写法,带有等号、大括号和返回值类型的形式

def myFunc(p1: Int) : Int = { //something } def myFunc(p1: Int) : Unit = { //something }

二、非unit返回值的情形下,省略返回值,让程序根据代码块,自行判断。注意,这里等号仍是要的

def myFunc(p1: Int) = { //something }

三、unit返回值的状况下,直接省略返回值类型和等号

def myFunc(var p1 : Int) { //something // return unit }

四、函数只有一行的情形下省略返回值和大括号

def max2(x: Int, y: Int) = if (x > y) x else y def greet() = println("Hello, world!")

函数调用

Scala 提供了多种不一样的函数调用方式:
如下是调用方法的标准格式:

functionName( 参数列表 )

若是函数使用了实例的对象来调用,咱们可使用相似java的格式 (使用 . 号):

[instance.]functionName( 参数列表 )

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

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

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

在进入函数内部前,传值调用方式就已经将参数表达式的值计算完毕,而传名调用是在函数内部进行参数表达式的值计算的。

这就形成了一种现象,每次使用传名调用时,解释器都会计算一次表达式的值。

object Test {
   def main(args: Array[String]) { delayed(time()); } def time() = { println("获取时间,单位为纳秒") System.nanoTime } def delayed( t: => Long ) = { println("在 delayed 方法内") println("参数: " + t) t } }

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

在 delayed 方法内
获取时间,单位为纳秒
参数: 241550840475831
获取时间,单位为纳秒

分析可得,delayed()中的time()参数先是没有调用,在打印t时再计算调用的.

咱们把t: => Long改为t: Long,打印结果就变为:

获取时间,单位为纳秒
在 delayed 方法内
参数: 241550840475831

可能如今你们有些乱,若是把这里的t: => Long改为t: () => Long我想你们就清楚了,方便记忆,能够认为这里的t并非一个值类型参数,而是一个函数类型的参数.

指定函数参数名

只是调用的时候,使用=指定参数而已,好比:

object Test {
   def main(args: Array[String]) { printInt(b=5, a=7); } def printInt( a:Int, b:Int ) = { println("Value of a : " + a ); println("Value of b : " + b ); } }

可变参数

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

object Test {
   def main(args: Array[String]) { printStrings("Runoob", "Scala", "Python"); } def printStrings( args:String* ) = { var i : Int = 0; for( arg <- args ){ println("Arg value[" + i + "] = " + arg ); i = i + 1; } } }

递归函数

递归函数意味着函数能够调用它自己。经常使用的好比计算阶乘.

默认参数值

在定义函数的时候使用=设置默认值,如:

def addInt( a:Int=5, b:Int=7 ) : Int = { }

高阶函数(Higher-Order Function)

Scala 中容许使用高阶函数,高阶函数可使用其余函数做为参数,或者使用函数做为输出结果。

内嵌函数

我么能够在 Scala 函数内定义函数,定义在函数内的函数称之为局部函数。

匿名函数

Scala 中定义匿名函数的语法很简单,箭头左边是参数列表,右边是函数体。
使用匿名函数后,咱们的代码变得更简洁了。

var mul = (x: Int, y: Int) => x*y

偏应用函数

Scala 偏应用函数是一种表达式,你不须要提供函数须要的全部参数,只须要提供部分,或不提供所需参数。

以下实例,咱们打印日志信息:

import java.util.Date object Test { def main(args: Array[String]) { val date = new Date log(date, "message1" ) Thread.sleep(1000) log(date, "message2" ) Thread.sleep(1000) log(date, "message3" ) } def log(date: Date, message: String) = { println(date + "----" + message) } }

输出结果为:

Mon Dec 02 12:52:41 CST 2013----message1 Mon Dec 02 12:52:41 CST 2013----message2 Mon Dec 02 12:52:41 CST 2013----message3

咱们可使用偏应用函数优化以上方法,绑定第一个 date 参数,第二个参数使用下划线(_)替换缺失的参数列表,并把这个新的函数赋给变量。

以下:

import java.util.Date object Test { def main(args: Array[String]) { val date = new Date val logWithDateBound = log(date, _ : String) logWithDateBound("message1" ) Thread.sleep(1000) logWithDateBound("message2" ) Thread.sleep(1000) logWithDateBound("message3" ) } def log(date: Date, message: String) = { println(date + "----" + message) } }

函数柯里化(Function Currying)

柯里化(Currying)指的是将原来接受两个参数的函数变成新的接受两次一个参数的函数的过程。

以下,首先咱们定义一个函数:

def add(x:Int,y:Int)=x+y

那么咱们应用的时候,应该是这样用:add(1,2)调用方法.
如今咱们把这个函数变一下形:

def add(x:Int)(y:Int) = x + y

咱们使用add(1)(2),最后结果都同样是3,这种方式(过程)就叫柯里化。

Scala 闭包

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

完整实例:

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 } 

是其实和js的闭包同样.

Scala 字符串

实例:

object Test {
   val greeting: String = "Hello,World!" def main(args: Array[String]) { println( greeting ) } }

以上实例定义了变量 greeting,为字符串常量,它的类型为 String (java.lang.String)。
在 Scala 中,字符串的类型其实是 Java String,Scala自己没有 String 类。

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

建立字符串

建立字符串实例以下:

var greeting = "Hello World!"; 或 var greeting:String = "Hello World!";

字符串链接

String 类中使用 concat() 方法来链接两个字符串:
string1.concat(string2)
一样你也可使用加号(+)来链接.string1 + string2

StringBuilder 类

object Test { def main(args: Array[String]) { val buf = new StringBuilder; buf += 'a' //+= 对字符 buf ++= "bcdef" //++= 对字符串 println( "buf is : " + buf.toString ); } }

StringBuffer 类

StringBuffer 相似,就不说了.

StringBuilder和StringBuffer区别是历代面试重点考点,主要区别以下.

StringBuilder StringBuffer
非线程安全的 线程安全的
效率高 效率低

上面只是基础问题,但实际使用中,不多用到StringBuffer,同时JVM对于(+)拼接也作了优化,在非循环状况下自动转为StringBuilder.本段引自

建立格式化字符串

  • 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) } }

字符串方法

和java基本一致!

Scala 数组

Scala 语言中提供的数组是用来存储固定大小同类型元素,数组对于每一门编程语言来讲都是重要的数据结构之一。

声明数组

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

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

var z = new Array[String](3)

而后就能够经过索引来访问每一个元素:

z(0) = "Runoob" z(1) = "Baidu" z(4/2) = "Google"

咱们也可使用如下方式来定义一个数组:即定义时直接赋值...

var z = Array("Runoob", "Baidu", "Google")

处理数组

数组的元素类型和数组的大小都是肯定的,因此当处理数组元素时候,咱们一般使用基本的 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); } }

多维数组

很少介绍

合并数组

如下实例中,咱们使用 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 ) } } }

建立区间数组

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

import Array._ object Test { def main(args: Array[String]) { var myList1 = Array.range(10, 20, 2) var myList2 = Array.range(10,20) // 输出全部数组元素 for ( x <- myList1 ) { print( " " + x ) } println() for ( x <- myList2 ) { print( " " + x ) } } }

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

10 12 14 16 18
10 11 12 13 14 15 16 17 18 19

Scala Collection

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

可变集合能够在适当的地方被更新或扩展。这意味着你能够修改,添加,移除一个集合的元素。

而不可变集合类,相比之下,永远不会改变。不过,你仍然能够模拟添加,移除或更新操做。可是这些操做将在每一种状况下都返回一个新的集合,同时使原来的集合不发生改变。

集合 描述
List(列表) 其元素以线性方式存储,集合中能够存放重复对象。
Set(集合) 最简单的一种集合。集合中的对象不按特定的方式排序,而且没有重复对象。
Map(映射) 把键对象和值对象映射的集合,它的每个元素都包含一对键对象和值对象。
元组 元组是不一样类型的值的集合
Option Option[T] 表示有可能包含值的容器,也可能不包含值。
Iterator(迭代器) 迭代器不是一个容器,更确切的说是逐一访问容器内元素的方法。

下面是各个集合的简单实现:

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

// 定义 Set var 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)

Iterator(迭代器)

迭代器 it 的两个基本操做是 next 和 hasNext。

  • 调用 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()) } } }
  • it.min 和 it.max 方法从迭代器中查找最大与最小元素;
  • it.size 或 it.length 方法来查看迭代器中的元素个数;

Scala 类和对象

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

实例以下:

object StudyClass {

  def main(args: Array[String]) { val pt = new Point(10, 20); // 移到一个新的位置 pt.move(10, 10); } } 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); } }

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); } }

Scala 单例对象

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

Scala 中使用单例模式时,除了定义的类以外,还要定义一个同名的 object 对象,它和类的区别是,object对象不能带参数。
当单例对象与某个类共享同一个名称时,他被称做是这个类的伴生对象:companion object。你必须在同一个源文件里定义类和它的伴生对象。类被称为是这个单例对象的伴生类:companion class。

类和它的伴生对象能够互相访问其私有成员。

单例对象实例

class singleObject(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 singleObject(10, 20) printPoint() def printPoint(){ println ("x 的坐标点 : " + point.x); println ("y 的坐标点 : " + point.y); } } }

单例对象实例

// 私有构造方法 class CompanionObject private(val color:String) { println("建立" + this) override def toString(): String = "颜色标记:"+ color } // 伴生对象,与类共享名字,能够访问类的私有属性和方法 object CompanionObject{ private val markers: Map[String, CompanionObject] = Map( "red" -> new CompanionObject("red"), "blue" -> new CompanionObject("blue"), "green" -> new CompanionObject("green") ) def apply(color:String) = { if(markers.contains(color)) Some(markers(color)) else None } def getCompanionObject(color:String) = { if(markers.contains(color)) Some(markers(color)) else None } def main(args: Array[String]) { println(CompanionObject("red")) // 单例函数调用,省略了.(点)符号 println(CompanionObject getCompanionObject "blue") // println(CompanionObject.getCompanionObject("blue")) } }

Scala Trait(特征)

Scala Trait(特征) 至关于 Java 的接口,实际上它比接口还功能强大。
与接口不一样的是,它还能够定义属性和方法的实现。
通常状况下Scala的类只可以继承单一父类,可是若是是 Trait(特征) 的话就能够继承多个,从结果来看就是实现了多重继承。

简单实例:

trait Equal {
  def isEqual(x: Any): Boolean def isNotEqual(x: Any): Boolean = !isEqual(x) }

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

如下展现Trait的完整实例:

trait StudyTrait {
  def isEqual(x: Any): Boolean def isNotEqual(x: Any): Boolean = !isEqual(x) } class Point(xc: Int, yc: Int) extends StudyTrait { var x: Int = xc var y: Int = yc def isEqual(obj: Any) = obj.isInstanceOf[Point] && obj.asInstanceOf[Point].x == x } object Test2 { def main(args: Array[String]) { val p1 = new Point(2, 3) val p2 = new Point(2, 3) val p3 = new Point(3, 3) println(p1.isNotEqual(p2)) println(p1.isNotEqual(p3)) println(p1.isNotEqual(2)) } }

特征构造顺序

特征也能够有构造器,由字段的初始化和其余特征体中的语句构成。

构造器的执行顺序:

  • 调用基类的构造器;
  • 特征构造器在基类构造器以后、类构造器以前执行;
  • 特征由左到右被构造;
  • 每一个特征当中,父特征先被构造;
  • 若是多个特征共有一个父特征,父特征不会被重复构造
  • 全部特征被构造完毕,子类被构造。

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

上面那句话说人话就是,最右边的特质最早实例化,当其中有super字样调用方法时,调用的是它左侧的类方法,若是左侧与它不是同一组,则真正调用父类方法.也就是super是动态调用的.

Scala 模式匹配

模式匹配是FP语言的一大利器,务必学好,本篇仅是入门.

一个模式匹配包含了一系列备选项,每一个都开始于关键字 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" } }

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

咱们还能够匹配不一样类型,像这样:

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" } }

使用样例类

使用了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) }

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

  • 构造器的每一个参数都成为private[this] val,除非显式被声明为var,可是并不推荐这么作;
  • 在伴生对象中提供了apply方法,因此能够不使用new关键字就可构建对象,apply就像Object的构造函数同样,使用对象名会被自动调用;
  • 提供unapply方法使模式匹配能够工做;
  • 生成toString、equals、hashCode和copy方法,除非显示给出这些方法的定义。

Scala 正则表达式

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

import scala.util.matching.Regex object StudyRegex { def main(args: Array[String]) { val pattern = "Scala".r() val str = "Scala is Scalable and cool" println(pattern.findFirstIn(str)) } }

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

Some(Scala)

实例中使用 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(",")) // 使用逗号 , 链接返回结果 } }

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

Scala,scala

若是你须要将匹配的文本替换为指定的关键词,可使用 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")) } }

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

Java is scalable and cool

Scala 异常处理

抛出异常

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

throw new IllegalArgumentException

捕获异常

异常捕捉的机制与其余语言中同样,若是有异常发生,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") } } finally { println("Exiting finally...") } } }

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

Missing file exception Exiting finally...

Scala 提取器(Extractor)

提取器是从传递给它的对象中提取出构造该对象的参数。
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 } } }

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

Apply 方法 : Zara@gmail.com Unapply 方法 : Some((Zara,gmail.com)) Unapply 方法 : None

以上对象定义了两个方法: apply 和 unapply 方法。经过 apply 方法咱们无需使用 new 操做就能够建立对象。因此你能够经过语句 Test("Zara", "gmail.com") 来构造一个字符串 "Zara@gmail.com"。
unapply方法算是apply方法的反向操做:unapply接受一个对象,而后从对象中提取值,提取的值一般是用来构造该对象的值。实例中咱们使用 Unapply 方法从对象中提取用户名和邮件地址的后缀。

提取器使用模式匹配

在咱们实例化一个类的时,能够带上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 }

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

10
10 是 5 的两倍!

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("BarryW") writer.close() } }

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

从屏幕上读取用户输入

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

object Test {
   def main(args: Array[String]) { print("请输入: " ) val line = Console.readLine println("谢谢,你输入的是: " + line) } }

从文件上读取内容

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

import scala.io.Source

object Test { def main(args: Array[String]) { println("文件内容为:" ) Source.fromFile("test.txt" ).foreach{ print } } }

以上都是Scala中最简单基础的入门,以及一些在Java等面向对象程序中比较经常使用的功能, 我接下来的文章中,会继续介绍Scala的一些高级功能和别扭的语法糖, Scala就是经过那些别扭的语法大大缩减了代码量, 而且使程序更加易写易读. 但愿本文可让你们感觉到FP简洁不简单的魅力.

相关文章
相关标签/搜索