Scala基础语法总结:
Scala 与 Java 的最大区别是:Scala 语句末尾的分号 ; 是可选的。如果一行里写多个语句那么分号是需要的 val s = "菜鸟教程"; println(s)
A:
区分大小写 - Scala是大小写敏感的,这意味着标识Hello 和 hello在Scala中会有不同的含义。
def main(args: Array[String]) - Scala程序从main()方法开始处理,这是每一个Scala程序的强制程序入口部分。
标识符
字符数字和符号
字符数字使用字母或是下划线开头,后面可以接字母或是数字,符号"$"在 Scala 中也看作为字母。然而以"$"开头的标识符为保留的 Scala 编译器产生的标志符使用,应用程序应该避免使用"$"开始的标识符,以免造成冲突
在 Scala 中你不能使用 Thread.yield()是因为 yield 为 Scala 中的关键字, 你必须使用 Thread.`yield`()来使用这个方法。
Scala 包
Scala 使用 package 关键字定义包某个包中有两种方式:
第一种方法Java 一样
package com.runoob
class HelloWorld
第二种方法有些类似 C#,可以在一个文件中定义多个包。如:
package com.runoob {
class HelloWorld
}
引用包
import 关键字引用包 import语句可以出现在任何地方,而不是只能在文件顶部。import的效果从开始延伸到语句块的结束。这可以大幅减少名称冲突的可能性
import java.awt.Color // 引入Color
import java.awt._ // 引入包内所有成员
def handler(evt: event.ActionEvent) { // java.awt.event.ActionEvent
... // 因为引入了java.awt,所以可以省去前面的部分
}
//引用多个包
import java.awt.{Color, Font}
// 重命名成员
import java.util.{HashMap => JavaHashMap}
// 隐藏成员
import java.util.{HashMap => _, _} // 引入了util包的所有成员,但是HashMap被隐藏了
默认情况下,Scala 总会引入 java.lang._ 、 scala._ 和 Predef._
B:
Scala 数据类型
Unit 表示无值,和其他语言中void等同。用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()。
Any是所有其他类的超类
AnyRef类是Scala里所有引用类(reference class)的基类
Nothing类型在Scala的类层级的最低端;它是任何其他类型的子类型。
符号字面量 '<标识符>
符号字面量 'x 是表达式 scala.Symbol("x") 的简写
如;
package scala
final case class Symbol private (name: String) {
override def toString: String = "'" + name
}
多行字符串用三个双引号来表示分隔符,格式为:""" ... """。
Null 值
空值是 scala.Null 类型。
Scala.Null和scala.Nothing是用统一的方式处理Scala面向对象类型系统的某些"边界情况"的特殊类型。
Null类是null引用对象的类型,它是每个引用类(继承自AnyRef的类)的子类。Null不兼容值类型
C:
Scala 变量
在 Scala 中,使用关键词 "var" 声明变量,使用关键词 "val" 声明常量
var myVar : String = "Foo"
val myVal : String = "Foo"
变量类型声明
变量的类型在变量名之后等号之前声明。定义变量的类型的语法格式如下:[]可选的
var VariableName : DataType [= Initial Value]
或
val VariableName : DataType [= Initial Value]
如:
var myVar :Int;
val myVal :String;
变量类型引用
在 Scala 中声明变量和常量不一定要指明数据类型,在没有指明数据类型的情况下,其数据类型是通过变量或常量的初始值推断出来的。
所以,如果在没有指明数据类型的情况下声明变量或常量必须要给出其初始值,否则将会报错。
var myVar = 10; myVar 会被推断为 Int 类型
val myVal = "Hello, Scala!"; myVal 会被推断为 String 类型。
Scala 多个变量声明
val xmax, ymax = 100 // xmax, ymax都声明为100
如果方法返回值是元组,我们可以使用 val 来声明一个元组:
val (myVar1: Int, myVar2: String) = Pair(40, "Foo")
也可以不指定数据类型
val (myVar1, myVar2) = Pair(40, "Foo")
D:
Scala 访问修饰符
Scala对象的访问级别都是 public。
私有(Private)成员
用private关键字修饰,带有此标记的成员仅在包含了成员定义的类或对象内部可见,同样的规则还适用内部类。
class Outer{
class Inner{
private def f(){println("f")}
class InnerMost{
f() // 正确
}
}
(new Inner).f() //错误
}
(new Inner).f( ) 访问不合法是因为 f 在 Inner 中被声明为 private,而访问不在类Inner之内。
但在 InnerMost 里访问f就没有问题的,因为这个访问包含在 Inner 类之内。
Java中允许这两种访问,因为它允许外部类访问内部类的私有成员。
保护(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() //错误
}
}
上例中,Sub 类对 f 的访问没有问题,因为 f 在 Super 中被声明为 protected,而 Sub 是 Super 的子类。相反,Other 对 f 的访问不被允许,因为 other 没有继承自 Super。而后者在 java 里同样被认可,因为 Other 与 Sub 在同一包里
公共(Public)成员
Scala中,如果没有指定任何的修饰符,则默认为 public。这样的成员在任何地方都可以被访问。
class Outer {
class Inner {
def f() { println("f") }
class InnerMost {
f() // 正确
}
}
(new Inner).f() // 正确因为 f() 是 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。
E:
Scala 运算符
算术运算符
关系运算符
逻辑运算符
位运算符
赋值运算符
F:
Scala IF...ELSE 语句
G:
Scala 循环
Scala 语言中默认是没有 break 语句,但是你在 Scala 2.8 版本后可以使用另外一种方式来实现 break 语句。当在循环中使用 break 语句,在执行到该语句时,就会中断循环并执行循环体之后的代码块。Scala 不支持 break 或 continue 语句
语法
Scala 中 break 的语法有点不大一样,格式如下:
// 导入以下包
import scala.util.control._
// 创建 Breaks 对象
val loop = new Breaks;
// 在 breakable 中循环
loop.breakable{
// 循环
for(...){
....
// 循环中断
loop.break;
}
}
如:
import scala.util.control._
object Test {
def main(args: Array[String]) {
var a = 0;
val numList = List(1,2,3,4,5,6,7,8,9,10);
val loop = new Breaks;
loop.breakable {
for( a <- numList){
println( "Value of a: " + a );
if( a == 4 ){
loop.break;
}
}
}
println( "After the loop" );
}
}
for循环
Scala 语言中 for 循环的语法:
for( var x <- Range ){
statement(s);
}
以上语法中,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 );
}
}
}
以下是一个使用了 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 );
}
}
}
$ scalac Test.scala
$ scala Test
value of a: 1
value of a: 2
value of a: 3
value of a: 4
value of a: 5
value of a: 6
value of a: 7
value of a: 8
value of a: 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 );
}
}
}
$ scalac Test.scala
$ scala Test
Value of a: 1
Value of b: 1
Value of a: 1
Value of b: 2
Value of a: 1
Value of b: 3
Value of a: 2
Value of b: 1
Value of a: 2
Value of b: 2
Value of a: 2
Value of b: 3
Value of a: 3
Value of b: 1
Value of a: 3
Value of b: 2
Value of a: 3
Value of b: 3
for 循环集合
for 循环集合的语法如下:
for( var x <- List ){
statement(s);
}
以上语法中, 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 );
}
}
}
执行以上代码输出结果为:
$ scalac Test.scala
$ scala Test
value of a: 1
value of a: 2
value of a: 3
value of a: 4
value of a: 5
value of a: 6
for 循环过滤
Scala 可以使用一个或多个 if 语句来过滤一些元素。
以下是在 for 循环中使用过滤器的语法。
for( var x <- List
if condition1; if condition2...
){
statement(s);
你可以使用分号(;)来为表达式添加一个或多个的过滤条件。
实例
以下是 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 );
}
}
}
执行以上代码输出结果为:
$ scalac Test.scala
$ scala Test
value of a: 1
value of a: 2
value of a: 4
value of a: 5
value of a: 6
value of a: 7
for 使用 yield
你可以将 for 循环的返回值作为一个变量存储。语法格式如下:
var retVal = for{ var x <- List
if condition1; if condition2...
}yield x
注意大括号中用于保存变量和条件,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 );
}
}
}
执行以上代码输出结果为:
$ scalac Test.scala
$ scala Test
value of a: 1
value of a: 2
value of a: 4
value of a: 5
value of a: 6
value of a: 7
H:
Scala 函数
函数是一组一起执行一个任务的语句。 您可以把代码划分到不同的函数中。如何划分代码到不同的函数中是由您来决定的,但在逻辑上,划分通常是根据每个函数执行一个特定的任务来进行的。
Scala 有函数和方法,二者在语义上的区别很小。Scala 方法是类的一部分,而函数是一个对象可以赋值给一个变量。换句话来说在类中定义的函数即是方法。
我们可以在任何地方定义函数,甚至可以在函数内定义函数(内嵌函数)。更重要的一点是 Scala 函数名可以由以下特殊字符:+, ++, ~, &,-, -- , \, /, : 等。
函数声明
Scala 函数声明格式如下:
def functionName ([参数列表]) : [return type]
如果你不写等于号和方法主体,那么方法会被隐式声明为"抽象(abstract)",包含它的类型于是也是一个抽象类型。
函数定义
方法定义由一个def 关键字开始,紧接着是可选的参数列表,一个冒号":" 和方法的返回类型,一个等于号"=",最后是方法的主体。
Scala 函数定义格式如下:
def functionName ([参数列表]) : [return type] = {
function body
return [expr]
}
以上代码中 return type 可以是任意合法的 Scala 数据类型。参数列表中的参数可以使用逗号分隔。
以下函数的功能是将两个传入的参数相加并求和:
object add{
def addInt( a:Int, b:Int ) : Int = {
var sum:Int = 0
sum = a + b
return sum
}
}
如果函数没有返回值,可以返回为 Unit,这个类似于 Java 的 void, 实例如下:
object Hello{
def printMe( ) : Unit = {
println("Hello, Scala!")
}
}
函数调用
Scala 提供了多种不同的函数调用方式:
以下是调用方法的标准格式:
functionName( 参数列表 )
如果函数使用了实例的对象来调用,我们可以使用类似java的格式 (使用 . 号):
[instance.]functionName( 参数列表 )
以上实例演示了定义与调用函数的实例:
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
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
Returned Value : 12
Scala 函数 - 可变参数
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;
}
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
Arg value[0] = Runoob
Arg value[1] = Scala
Arg value[2] = Python
Scala 函数 - 默认参数值
Scala 可以为函数参数指定默认参数值,使用了默认参数,你在调用函数的过程中可以不需要传递参数,这时函数就会调用它的默认参数值,如果传递了参数,则传递值会取代默认值。实例如下:
object Test {
def main(args: Array[String]) {
println( "返回值 : " + addInt() );
}
def addInt( a:Int=5, b:Int=7 ) : Int = {
var sum:Int = 0
sum = a + b
return sum
}
}
执行
$ scalac Test.scala
$ scala Test
返回值 : 12
Scala 函数嵌套
我么可以在 Scala 函数内定义函数,定义在函数内的函数称之为局部函数。
以下实例我们实现阶乘运算,并使用内嵌函数:
object Test {
def main(args: Array[String]) {
println( factorial(0) )
println( factorial(1) )
println( factorial(2) )
println( factorial(3) )
}
def factorial(i: Int): Int = {
def fact(i: Int, accumulator: Int): Int = {
if (i <= 1)
accumulator
else
fact(i - 1, i * accumulator)
}
fact(i, 1)
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
1
1
2
6
Scala 偏应用函数
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)
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
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
实例中,log() 方法接收两个参数:date 和 message。我们在程序执行时调用了三次,参数 date 值都相同,message 不同。
我们可以使用偏应用函数优化以上方法,绑定第一个 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)
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
Mon Dec 02 12:53:56 CST 2013----message1
Mon Dec 02 12:53:56 CST 2013----message2
Mon Dec 02 12:53:56 CST 2013----message3
高阶函数(Higher-Order Function)就是操作其他函数的函数。
Scala 中允许使用高阶函数, 高阶函数可以使用其他函数作为参数,或者使用函数作为输出结果。
以下实例中,apply() 函数使用了另外一个函数 f 和 值 v 作为参数,而函数 f 又调用了参数 v:
object Test {
def main(args: Array[String]) {
println( apply( layout, 10) )
}
// 函数 f 和 值 v 作为参数,而函数 f 又调用了参数 v
def apply(f: Int => String, v: Int) = f(v)
def layout[A](x: A) = "[" + x.toString() + "]"
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
[10]
Scala 匿名函数
Scala 中定义匿名函数的语法很简单,箭头左边是参数列表,右边是函数体,参数的类型是可省略的,Scala 的类型推测系统会推测出参数的类型。使用匿名函数后,我们的代码变得更简洁了。
下面的表达式就定义了一个接受一个Int类型输入参数的匿名函数:
var inc = (x:Int) => x+1
上述定义的匿名函数,其实是下面这种写法的简写:
def add2 = new Function1[Int,Int]{
def apply(x:Int):Int = x+1;
}
以上实例的 inc 现在可作为一个函数,使用方式如下:
var x = inc(7)-1
同样我们可以在匿名函数中定义多个参数:
var mul = (x: Int, y: Int) => x*y
mul 现在可作为一个函数,使用方式如下:
println(mul(3, 4))
我们也可以不给匿名函数设置参数,如下所示:
var userDir = () => { System.getProperty("user.dir") }
userDir 现在可作为一个函数,使用方式如下:
println( userDir )
Scala 函数柯里化(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,这种方式(过程)就叫柯里化。
实现过程
add(1)(2) 实际上是依次调用两个普通函数(非柯里化函数),第一次调用使用一个参数 x,返回一个函数类型的值,第二次使用参数y调用这个函数类型的值。
实质上最先演变成这样一个方法:
def add(x:Int)=(y:Int)=>x+y
那么这个函数是什么意思呢? 接收一个x为参数,返回一个匿名函数,该匿名函数的定义是:接收一个Int型参数y,函数体为x+y。现在我们来对这个方法进行调用。
val result = add(1)
回一个result,那result的值应该是一个匿名函数:(y:Int)=>1+y
所以为了得到结果,我们继续调用result。
val sum = result(2)
最后打印出来的结果就是3。
完整实例
下面是一个完整实例:
object Test {
def main(args: Array[String]) {
val str1:String = "Hello, "
val str2:String = "Scala!"
println( "str1 + str2 = " + strcat(str1)(str2) )
}
def strcat(s1: String)(s2: String) = {
s1 + s2
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
str1 + str2 = Hello, Scala!
I:
Scala 闭包
闭包是一个函数,返回值依赖于声明在函数外部的一个或多个变量。
闭包通常来讲可以简单的认为是可以访问一个函数里面局部变量的另外一个函数。
如下面这段匿名的函数:
val multiplier = (i:Int) => i * 10
函数体内有一个变量 i,它作为函数的一个参数。如下面的另一段代码:
val multiplier = (i:Int) => i * factor
在 multiplier 中有两个变量:i 和 factor。其中的一个 i 是函数的形式参数,在 multiplier 函数被调用时,i 被赋予一个新的值。然而,factor不是形式参数,而是自由变量,考虑下面代码:
var factor = 3
val multiplier = (i:Int) => i * factor
这里我们引入一个自由变量 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
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
muliplier(1) value = 3
muliplier(2) value = 6
J:
Scala 字符串
我们前面提到过 String 对象是不可变的,如果你需要创建一个可以修改的字符串,可以使用 String Builder 类,如下实例:
object Test {
def main(args: Array[String]) {
val buf = new StringBuilder;
buf += 'a'
buf ++= "bcdef"
println( "buf is : " + buf.toString );
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
buf is : abcdef
K:
Scala 数组
声明数组
以下是 Scala 数组声明的语法格式:
var z:Array[String] = new Array[String](3)
或
var z = new Array[String](3)
多维数组
多维数组一个数组中的值可以是另一个数组,另一个数组的值也可以是一个数组。矩阵与表格是我们常见的二维数组。
以上是一个定义了二维数组的实例:
var myMatrix = ofDim[Int](3,3)
合并数组
以下实例中,我们使用 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 )
}
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
1.9
2.9
3.4
3.5
8.9
7.9
0.4
1.5
创建区间数组
以下实例中,我们使用了 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 )
}
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
10 12 14 16 18
10 11 12 13 14 15 16 17 18 19
L:
Scala Collection
Scala 集合分为可变的和不可变的集合。
可变集合可以在适当的地方被更新或扩展。这意味着你可以修改,添加,移除一个集合的元素。
而不可变集合类,相比之下,永远不会改变。不过,你仍然可以模拟添加,移除或更新操作。但是这些操作将在每一种情况下都返回一个新的集合,同时使原来的集合不发生改变
1 Scala List(列表)
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)
)
构造列表的两个基本单位是 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
列表基本操作
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 )
}
}
执行以上代码,输出结果为:
$ vim Test.scala
$ scala Test.scala
第一网站是 : Runoob
最后一个网站是 : List(Google, Baidu)
查看列表 site 是否为空 : false
查看 nums 是否为空 : true
连接列表
你可以使用 ::: 运算符或 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 )
// 使用 Set.:::() 方法
fruit = site1.:::(site2)
println( "site1.:::(site2) : " + fruit )
// 使用 concat 方法
fruit = List.concat(site1, site2)
println( "List.concat(site1, site2) : " + fruit )
}
}
执行以上代码,输出结果为:
$ 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)
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 )
}
}
执行以上代码,输出结果为:
$ vim Test.scala
$ scala Test.scala
site : List(Runoob, Runoob, Runoob)
num : List(2, 2, 2, 2, 2, 2, 2, 2, 2, 2)
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 )
}
}
执行以上代码,输出结果为:
$ 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))
List.reverse
List.reverse 用于将列表的顺序反转,实例如下:
object Test {
def main(args: Array[String]) {
val site = "Runoob" :: ("Google" :: ("Baidu" :: Nil))
println( "site 反转前 : " + site )
println( "site 反转前 : " + site.reverse )
}
}
执行以上代码,输出结果为:
$ vim Test.scala
$ scala Test.scala
site 反转前 : List(Runoob, Google, Baidu)
site 反转前 : List(Baidu, Google, Runoob)
参考 API文档
2 Scala Set(集合)
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)
如果需要使用可变集合需要引入 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
注意: 虽然可变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 )
}
}
执行以上代码,输出结果为:
$ vim Test.scala
$ scala Test.scala
第一网站是 : Runoob
最后一个网站是 : Set(Google, Baidu)
查看列表 site 是否为空 : false
查看 nums 是否为空 : true
连接集合
你可以使用 ++ 运算符或 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 )
}
}
执行以上代码,输出结果为:
$ vim Test.scala
$ scala Test.scala
site1 ++ site2 : Set(Faceboook, Taobao, Google, Baidu, Runoob)
site1.++(site2) : Set(Faceboook, Taobao, Google, Baidu, Runoob)
查找集合中最大与最小元素
你可以使用 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 )
}
}
执行以上代码,输出结果为:
$ vim Test.scala
$ scala Test.scala
Set(5,6,9,20,30,45) 集合中的最小元素是 : 5
Set(5,6,9,20,30,45) 集合中的最大元素是 : 45
交集
你可以使用 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) )
}
}
执行以上代码,输出结果为:
$ vim Test.scala
$ scala Test.scala
num1.&(num2) : Set(20, 9)
num1.intersect(num2) : Set(20, 9)
参考 API文档
3 Scala Map(映射)
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")
定义 Map 时,需要为键值对定义类型。如果需要添加 key-value 对,可以使用 + 号,如下所示:
A += ('I' -> 1)
A += ('J' -> 5)
A += ('K' -> 10)
A += ('L' -> 100)
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 )
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
colors 中的键为 : Set(red, azure, peru)
colors 中的值为 : MapLike(#FF0000, #F0FFFF, #CD853F)
检测 colors 是否为空 : false
检测 nums 是否为空 : true
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 )
}
}
执行以上代码,输出结果为:
$ 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)
输出 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) )}
}
}
执行以上代码,输出结果为:
$ 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
查看 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 键不存在")
}
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
runoob 键存在,对应的值为 :http://www.runoob.com
baidu 键存在,对应的值为 :http://www.baidu.com
google 键不存在
参考 API文档
4 Scala 元组
元组是不同类型的值的集合
与列表一样,元组也是不可变的,但与列表不同的是元组可以包含不同类型的元素。
元组的值是通过将单个的值包含在圆括号中构成的。例如:
val t = (1, 3.14, "Fred")
以上实例在元组中定义了三个元素,对应的类型分别为[Int, Double, java.lang.String]。
此外我们也可以使用以上方式来定义:
val t = new Tuple3(1, 3.14, "Fred")
元组的实际类型取决于它的元素的类型,比如 (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)
我们可以使用 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 )
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
元素之和为: 10
迭代元组
你可以使用 Tuple.productIterator() 方法来迭代输出元组的所有元素:
object Test {
def main(args: Array[String]) {
val t = (4,3,2,1)
t.productIterator.foreach{ i =>println("Value = " + i )}
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
Value = 4
Value = 3
Value = 2
Value = 1
元组转为字符串
你可以使用 Tuple.toString() 方法将元组的所有元素组合成一个字符串,实例如下:
object Test {
def main(args: Array[String]) {
val t = new Tuple3(1, "hello", Console)
println("连接后的字符串为: " + t.toString() )
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
连接后的字符串为: (1,hello,scala.Console$@4dd8dc3)
元素交换
你可以使用 Tuple.swap 方法来交换元组的元素。如下实例:
object Test {
def main(args: Array[String]) {
val t = new Tuple2("www.google.com", "www.runoob.com")
println("交换后的元组: " + t.swap )
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
交换后的元组: (www.runoob.com,www.google.com)
5 Scala Option
Option[T] 表示有可能包含值的容器,也可能不包含值。
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
在上面的代码中,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
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
sites.get( "runoob" ) : Some(www.runoob.com)
sites.get( "baidu" ) : None
你也可以通过模式匹配来输出匹配值。实例如下:
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 => "?"
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
show(sites.get( "runoob")) : www.runoob.com
show(sites.get( "baidu")) : ?
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) )
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
a.getOrElse(0): 5
b.getOrElse(10): 10
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 )
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
a.isEmpty: false
b.isEmpty: true
6 Scala Iterator(迭代器)
迭代器不是一个容器,更确切的说是逐一访问容器内元素的方法。
Scala 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())
}
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
Baidu
Google
Runoob
Taobao
查找最大与最小元素
你可以使用 it.min 和 it.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 )
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
最大元素是:90
最小元素是:2
获取迭代器的长度
你可以使用 it.size 或 it.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 )
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
ita.size 的值: 6
itb.length 的值: 6
M:
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);
}
}
Scala中的类不声明为public,一个Scala源文件中可以有多个类。
以上实例的类定义了两个变量 x 和 y ,一个方法: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);
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
x 的坐标点: 20
y 的坐标点: 30
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 使用 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);
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
x 的坐标点 : 20
y 的坐标点 : 30
z 的坐标点 : 20
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)
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
Employee[name=Fred][salary=50000.0]
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);
}
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
x 的坐标点 : 10
y 的坐标点 : 20
伴生对象实例
/* 文件名:Marker.scala
* author:菜鸟教程
* url:www.runoob.com
*/
// 私有构造方法
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")
}
}
执行以上代码,输出结果为:
$ scalac Marker.scala
$ scala Marker
创建颜色标记:red
创建颜色标记:blue
创建颜色标记:green
颜色标记:red
颜色标记:blue
M:
Scala Trait(特征)
Scala Trait(特征) 相当于 Java 的接口,实际上它比接口还功能强大。
与接口不同的是,它还可以定义属性和方法的实现。
一般情况下Scala的类只能够继承单一父类,但是如果是 Trait(特征) 的话就可以继承多个,从结果来看就是实现了多重继承。
Trait(特征) 定义的方式与类类似,但它使用的关键字是 trait,如下所示:
trait Equal {
def isEqual(x: Any): Boolean
def isNotEqual(x: Any): Boolean = !isEqual(x)
}
以上Trait(特征)由两个方法组成:isEqual 和 isNotEqual。isEqual 方法没有定义方法的实现,isNotEqual定义了方法的实现。子类继承特征可以实现未被实现的方法。所以其实 Scala Trait(特征)更像 Java 的抽象类。
以下演示了特征的完整实例:
/* 文件名:Test.scala
* author:菜鸟教程
* url:www.runoob.com
*/
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))
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
false
true
true
特征构造顺序
特征也可以有构造器,由字段的初始化和其他特征体中的语句构成。这些语句在任何混入该特征的对象在构造是都会被执行。
构造器的执行顺序:
调用超类的构造器;
特征构造器在超类构造器之后、类构造器之前执行;
特质由左到右被构造;
每个特征当中,父特质先被构造;
如果多个特征共有一个父特质,父特质不会被重复构造
所有特征被构造完毕,子类被构造。
构造器的顺序是类的线性化的反向。线性化是描述某个类型的所有超类型的一种技术规格。
N:
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"
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
many
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"
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
2
many
one
scala.Int
实例中第一个 case 对应整型数值 1,第二个 case 对应字符串值 two,第二个 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)
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
Hi Alice!
Hi Bob!
Age: 32 year, name: Charlie?
在声明样例类时,下面的过程自动发生了:
构造器的每个参数都成为val,除非显式被声明为var,但是并不推荐这么做;
在伴生对象中提供了apply方法,所以可以不使用new关键字就可构建对象;
提供unapply方法使模式匹配可以工作;
生成toString、equals、hashCode和copy方法,除非显示给出这些方法的定义。
O:
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)
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
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(",")) // 使用逗号 , 连接返回结果
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
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"))
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
Java is scalable and cool
50-
28.5
22.5
50
11.5
11.5
34
N:
Scala 的异常处理和其它语言比如 Java 类似。
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")
}
}
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
Missing file exception
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...")
}
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
Missing file exception
Exiting finally...
O:
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
}
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
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 方法从对象中提取用户名和邮件地址的后缀。
实例中 unapply 方法在传入的字符串不是邮箱地址时返回 None。代码演示如下:
unapply("Zara@gmail.com") 相等于 Some("Zara", "gmail.com")
unapply("Zara Ali") 相等于 None
提取器使用模式匹配
在我们实例化一个类的时,可以带上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
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
10
10 是 5 的两倍!
P:
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()
}
}
执行以上代码,会在你的当前目录下生产一个 test.txt 文件,文件内容为"菜鸟教程":
$ scalac Test.scala
$ scala Test
$ cat test.txt
菜鸟教程
从屏幕上读取用户输入
有时候我们需要接收用户在屏幕输入的指令来处理程序。实例如下:
object Test {
def main(args: Array[String]) {
print("请输入菜鸟教程官网 : " )
val line = Console.readLine
println("谢谢,你输入的是: " + line)
}
}
执行以上代码,屏幕上会显示如下信息:
$ scalac Test.scala
$ scala Test
请输入菜鸟教程官网 : www.runoob.com
谢谢,你输入的是: www.runoob.com
从文件上读取内容
从文件读取内容非常简单。我们可以使用 Scala 的 Source 类及伴生对象来读取文件。以下实例演示了从 "test.txt"(之前已创建过) 文件中读取内容:
import scala.io.Source
object Test {
def main(args: Array[String]) {
println("文件内容为:" )
Source.fromFile("test.txt" ).foreach{
print
}
}
}
执行以上代码,输出结果为:
$ scalac Test.scala
$ scala Test
文件内容为:
菜鸟教程
总结:
1.scala是函数式编程
变量
val v2 = 3 val 有getter方法
var v2 = 3 var 有getter 和setter方法
def v3 = v1*v2 只是定义了v1*v2的表达式,在使用的时候求职
懒加载变量:当使用的时候,才显示
lazy val lazyVal = (println("I'm too lazy."))
使用:
val x = lazyVal //会打印“I'm too lazy.”
2.Any超类 --{AnyRef(引用类型的父类型), AnyVal(所有值类型的父类型)}....... 最后面是nothing(底类型)
AnyRef引用类型的父类型:String Iterable seq List 底类型对应 NULL
AnyVal所有值类型的父类型:Int Unit Double 底类型对应NULL
Any对应的底类型nothing
3.半生对象
ppackage com.bjsxt.study
class TestObject {
val t2 = "lskjdfkljd"
var t=123
def func01() = {
println("gaga");
}
}
object TestObject {
val t1 = 123;
var ssssgagag=1444;
val single = new TestObject()
def func02() = {
println("gaga")
}
def main(args: Array[String]) {
val t1 = new TestObject()
println(t1.t2)
t1.func01()
TestObject.func02()
println(TestObject.t1)
println(TestObject.ssssgagag)
}
}
为实现单例,在静态object中new 一个类的对象--此时初始化类的除了def所有的成员变量
这个对象(t1),可以访问class中的成员和方法
println(t1.t2)
t1.func01()
而object可以以类的形式访问自己的成员和方法
TestObject.func02()
println(TestObject.t1)
println(TestObject.ssssgagag)
4.函数
// 匿名函数作为参数,其实就是参数名,后面跟上参数类型,然后是表达式
def testf1(callback : (Int,Int)=>Int )={
println(callback(123,123))
}
调用:
// val tttt = (a:Int,b:Int)=>a*b; 传一个匿名函数的逻辑 保护值不被改变123,123
// testf1(tttt);
// testf1((a:Int,b:Int)=>a*b)
/ 能否看懂???
def sum(f : Int => Int) : (Int , Int) => Int = {
def sumF(a : Int , b : Int) : Int =
if (a >b ) 0 else f(a) + sumF( a + 1 , b)
sumF
}
解析:
f : Int => Int 传入一个匿名 返回(Int , Int) => Int 就是sumF
调用:
val temp = sum(x=>x+2) //传入f的逻辑
println(temp(2,3))//传如 sumF的参数
5.接口trait
package com.bjsxt.study
trait Listen {
val name: String
def listen() = {
println("You friend " + name + " is listening")
}
}
trait Read {
val name: String
def read() = {
println("You friend " + name + " is reading")
}
}
trait Speak {
val name: String
def speak() = {
println("You friend " + name + " is speaking.")
}
}
class Human(val name: String) {
def listen() = {
println(name + " is listening.")
}
}
class Animal(val name: String)
//继承要重写,加override 一般第一个写extends 其它不用写了 加with
class Cat(override val name: String) extends Animal(name: String) with Speak with Listen with Read {
override def toString(): String = " hello " + name + "! "
}
object TestFriend {
def main(args: Array[String]) {
// val f1 = new Listen();//java 接口很像 不能直接构造
val h1 = new Human("gaga")
h1.listen()
val h2 = new Human("xuruyun") with Speak //带上特质可以访问
h2.speak()
//这样scat就是Friend了
val scat = new Cat("hello kitty")
scat.listen()
scat.speak()
println(scat)
}
}
6.
/: 是flodleft
println(t./:(0)({
(sum,num)=>sum-num
}))
println(t.foldLeft(0)((sum,num)=>{
// print(sum+"--"+num+" ");
// sum-num;
// }))
/: 表示一个函数 0是初始值, 一次赋值给num 减去num
7.
// 1,2,3,5,5
println(t.reduce( _-_ ))
println(t.filter ( _>3 ))
println(t.take(3))
8,隐式函数
implicit def隐式方法
1.to 10 RichInt包装类
试图把Int转换成
object 一切的父类
如果是Student older
04柯丽华函数
implicit 隐式参数 默默的传进来,不用给参数
package com.bjsxt.study
/*
* 隐式参数
*/
implicit1
class SignPen{
def write(content: String) = println(content)
}
object ImplicitContext{
implicit val signPen = new SignPen
}
object Implicit04 {
//柯丽华函数
def signForExam(name:String)(implicit signPen : SignPen): Unit ={
signPen.write(name + " arrive in time.")
}
def main(args: Array[String]) {
import ImplicitContext._
signForExam("yasaka")
signForExam("tongyao")
}
}
只需要传入一个名字就可以了,pen是默认传进去
隐式函数
package com.bjsxt.study
/**
类型方法有但是参数不一致会尝试调用隐式转换
*/
class TicketHouse{
var ticketNumber = 0
def buySpecialTicket(p:SpecialPerson) = {
ticketNumber += 1
"T-" + ticketNumber
}
}
object Implicit03 {
implicit def object2SpecialPerson(obj:Object): SpecialPerson ={
if(obj.getClass == classOf[Student]){
val stu = obj.asInstanceOf[Student]
new SpecialPerson(stu.name)
}else if (obj.getClass == classOf[Older]){
val older = obj.asInstanceOf[Older]
new SpecialPerson(older.name)
}else{
Nil
}
}
def main(args: Array[String]) {
val ticketHouse = new TicketHouse
val yasaka = new Student("yasaka")
println(ticketHouse.buySpecialTicket(yasaka))
}
}