有了可运行的环境,就需要写一些简单的语句来认识一下Scala,本文没有写那么详细,只是为了方便查看、唤起回忆
(1)变量的定义方法
Scala有两种变量
var
val
val 类似于 Java 中的 final 变量,一旦初始化之后,不可以重新赋值
注意:在解释器中,可以用一个之前已经使用了的名字定义新的val
Scala 可以使用两种形式的标志符:字符数字和符号
字符数字:使用字母或是下划线开头,后面可以接字母或是数字
符号:包含一个或多个符号,如+,:,? 等
扩展:
如果你是个 Java 程序员,你会发现 Java 支持的基本数据类型,Scala 都有对应的支持,不过 Scala 的数据类型都是对象(比如整数),这些基本类型都可以通过隐式自动转换的形式支持更多的方法。
譬如:如果调用 (-1).abs() ,Scala 发现基本类型 Int 没有提供 abs()方法,但发现系统提供了从 Int 类型转换为 RichInt 的隐式自动转换,而 RichInt 具有 abs 方法,那么 Scala 就自动将 1 转换为 RichInt 类型,然后调用 RichInt 的 abs 方法。
隐式转换是一项compiler功能,在程序编译(compile)的时候由compiler来进行类型转换代码的产生和替代
一个类型T的隐式作用域就是组成这个类型的所有类的伴生对象(companion object)
(2)函数结构
def max(X:Int,Y:Int):Int={ if(X>Y) X else Y }
既不带参数也不返回有用结果的函数定义:
def greet()=println("Hello, World !")
如果希望函数返回某个值,但忘了方法定义中的 “=”,Scala 会忽略方法的返回值,而返回 Unit。
在 Scala 中可以省略掉没有参数的方法调用的空括号。
按照惯例,如果调用方法是为了利用方法的“副作用”,此时写上空括号;如果方法没有任何副作用(没有修改其它程序状态),可以省略掉括号。
(3)while循环
var i=0 while (i < args.length) { println (args(i)) i+=1 }
注意:Scala 不支持++i、i++ 运算,需要使用 i+=1 来自增
扩展:
def approximate(initialGuess: Double) : Double = { var guess = initialGuess while(!isGoodEnough(guess)) guess=improve(guess) guess }
从简洁度和避免使用 var 变量上看,可以使用函数化编程递归
def approximate(guess: Double) : Double = if (isGoodEnough(guess)) guess else approximate(improve(guess))
(4)foreach
使用 while 来实现循环,和使用 Java 实现无太大差异,而 Scala 是面向函数的语言,更好的方法是采用“函数式“风格来编写代码,如下:
args.foreach(arg=>println(arg))
利用 Scala 的缩写形式,如果一个函数只有一个参数并且只包含一个表达式,那么无需明确指明参数,这段代码可以写的更精简些,如下:
args.foreach( println)
(5)for
for(arg<-args) println(arg)
注意:上述语句中的arg是val类型的
可以在for语句中使用多个过滤器,即添加多个 if 语句
val filesHere = (new java.io.File(".")).listFiles for( file <-filesHere if file.isFile if file.getName.endsWith(".scala") ) println(file)
(6)使用new实例化对象
实例化过程中,可以使用数值或类型作为参数
使用数值实例化
val big=new java.math.BigInteger("123456") println(big)
使用类型实例化
val greetStrings:Array[String]=new Array[String](3) greetStrings(0)="Hello" greetStrings(1)="," greetStrings(2)="World! " for(i<-0 to 2) print(greetStrings(i))
注:Array是可变的,Scala访问数组的语法是使用()而非[]。
Array 的 zip 操作符可以用来将两个数组转换成二元组的数组
scala> Array( 1,2,3) zip Array("a","b")
res0: Array[(Int, String)] = Array((1,a), (2,b))
从上述代码可知:zip 分别取两个数组对应的元素组成一个新的二元祖
如果一个数组长度大于另外一个数组,多余的元素被忽略
使用类型作为参数,它的作用类似 Java 的 Generic 类型。
对比如下 Java 代码,可知 Scala 使用方括号来指明类型参数,而非尖括号。
LinkedList number = new LinkedList<Integer>();
(7)上述示例中 for 表达式使用了 0 to 2,其实质是:(0).to(2)。
Scala 中所有基本数据类型都是对象(和 Java 不同),因此 0 可以有方法(实际上调用的是 RichInt 的 to 方法),这种只有一个参数的方法可以使用操作符的写法(不用.和括号)
需要注意的是:该语法只有在明确指定方法调用的接受者时才有效,如:Console println 10
(8)Scala里所有的运算符都是方法调用
1+2
实质上是:(1).+(2)
运算符在 Scala 中并不是什么特殊的语法,任何 Scala 方法都可以作为运算符来使用,是否是运算符取决于你如何使用这个方法
当你使用 s.indexOf(‘o’)时,indexOf 不是一个运算符; 而写成 s indexOf ‘o’时, indexOf 就是一个运算符,因为使用了运算符的语法。
除了类似+的中缀运算符(运算符在两个操作数之间),还有前缀运算符和后缀运算符。
顾名思义,前缀运算符在操作数前面,比如 -7 中的“-”;后缀运算符在操作数的后面,比如 7 toLong 中的 toLong。
前缀和后缀运算符都使用一个操作数,而中缀运算符使用前后两个操作数。
Scala 中实现前缀和后缀运算符的方法都以 unary_-开头;比如: 表达式 -2.0 实际上调用 (2.0).unary_- 方法。
扩展;
Scala 的==和 Java 不同,scala 的==只用于比较两个对象的值是否相同,而对于引用类型的比较使用另外的操作符 eq 和 ne。
scala> val x =new String("abc") x: String = abc scala> val y =new String("abc") y: String = abc scala> x == y res0: Boolean = true scala> x eq y res1: Boolean = false scala> x ne y res2: Boolean = true
(9)用括号给变量传递一个或多个值参数时,Scala会把它转换成对apply方法的调用
这说明了为什么 Scala 使用()来访问数组元素:
在 Scala 中,数组和其它普通类一样,没有什么特别之处,前述示例中的 greetStrings(i) 被转化为 greetStrings.apply(i);
(10)当对带有括号并包括一到若干参数的变量赋值时,将调用变量的update方法,并以括号里的值和等号右边的对象作为参数
如:greetStrings(0)="Hello" 将被转化为 greetStrings.update(0,"Hello")
故前述示例可以写为如下方式:
val greetStrings =new Array[String](3) greetStrings.update(0,"Hello") greetStrings.update(1,",") greetStrings.update(2,"world! ") for(i <- 0 to 2) print(greetStrings.apply(i))
(11)List-不可变的同类对象序列,其索引基于0
Scala 中数组的元素是可以被修改的,如果需要使用不可修改的序列,Scala 提供了 Lists。
List 提供了一个 :: 方法用来向 List 中添加一个元素,:: 方法(操作符)是右操作符,也就是使用 :: 右边的对象来调用它的 :: 方法
val oneTowThree = 1 :: 2 ::3 :: Nil
println(oneTowThree)
Scala 规定所有以 : 开头的操作符都是右操作符,如果你自己定义以 : 开头的方法(操作符)也是右操作符
对某个列表调用方法时,似乎这个列表发生了改变,而实际上只是用新的值重建了列表然后再返回
Nil是空列表的简写
省略方法介绍
扩展:
面向函数的编程语言的一个特点是调用某个方法不应该有任何副作用:参数一定,调用方法后,返回一定的结果,而不去修改程序的其它状态(副作用)。
这样做的一个好处是方法和方法之间关联性较小,从而方法变得更可靠,且重用性高。
这个原则意味着变量需要设成不可修改的,进而避免了多线程的互锁问题。
(12)元组(Tuple)-不可变,可包含不同类型的元素,其索引基于1
Tuple 在方法需要返回多个结果时非常有用,可以使用 ._ 或者 索引 来访问元组的元素
val pair=(99,"Luftballons")
println(pair._1)
println(pair._2)
元组的实际类型取决于它的分量的类型,比如上面 pair 的类型为 Tuple2[Int, String],而 (‘u’, ’r’, ”the”, 1, 4, ”me”) 的类型为 Tuple6[Char, Char, String, Int, Int, String]。
目前 Scala 的元组支持的最大长度为 22,如果有需要,可以自己扩展更长的元组。
(13)特质(trait)
该概念接近于Java的接口(interface),所不同的是 Scala 中的 Trait 可以有方法的实现
(14)集(set)
Scala 提供了两种 Sets 类,分两个包定义 Mutable (可变)和 Immutable (不可变),使用同样名称的 Trait
如果使用可修改的集合类( Set 类型),需要使用全路径来指明 Set,如:scala.collection.mutalbe.Set。
缺省情况下 Set 为 Immutable Set。
不可变:
var tempSet=Set("he","she") tempSet+="it" println(tempSet.contains("kk"))
将上例中的 var 改为 val 会出现错误,代码如下,
val tempSet=Set("he","she") tempSet+="it" println(tempSet.contains("kk"))
错因:对不可变集使用+=,会对 tempSet 重新赋值,因此应该定义变量为var
可变:
import scala.collection.mutable.Set val tempSet=Set("he","she") tempSet+="it" println(tempSet.contains("kk"))
该处使用val却不会出现问题
(15)映射(map)
Scala 提供了 Mutable 和 Immutable 两种 Map 类型
用->和+=方法向Map里添加键值对
val romanNumeral = Map ( 1 -> "I", 2 -> "II", 3 -> "III", 4 -> "IV", 5 -> "V")
println (romanNumeral(2))
(16)任何对象都能调用->方法,并返回包含键值对的二元组
隐式转换
(17)
Scala 引入包的方式和 Java 类似,也是通过 import 语句。
如果需要引入多个类,Scala 使用 “_” 而非 “*”。
和 Java 相比,Scala 的 import 的使用更加灵活:
- 可以出现在文件中任何地方
- 可以 import 对象(singleton 或者普通对象)和 package 本身
- 支持对引入的对象重命名或者隐藏某些类型
使用=>重命名类型,如:
重新定义包名称
import java.{sql => S}
除 String 类型定义在 java.lang 包中,其它类型都定义在包 scala 中。
Scala的每个源文件都隐含了对包java.lang、包scala,以及单例对象Predef的成员引用,可以使用直接使用 Int,Short,String等,而无需再引入包或是使用全称。
StringBuilder 类定义在 scala 包和 java.lang 包中,后定义的 import 会覆盖前面的,因此如果不明确指明,StringBuilder 为 scala.StringBuilder 而非 java.lang.StringBuilder。
扩展:
Scala支持如下两种方式将代码放入包中:
A、
package shop.navigation class Navigator
B、
package shop.navigation { class Navigator }
第二种方法可以在一个文件中定义多个包。
(18)try-catch
Scala 的异常处理和 Java 类似,一个方法可以通过抛出异常而终止
Scala 捕获异常的方法:
import java.io.FileReader import java.io.FileNotFoundException import java.io.IOException try { val f = new FileReader("input.txt") } catch { case ex: FileNotFoundException => //handle missing file case ex: IOException => //handle other I/O error }
Scala 的 try-catch 可以返回结果,如下示例:
import java.net.URL import java.net.MalformedURLException
def urlFor(path:String) = try { new URL(path) } catch { case e: MalformedURLException => new URL("http://www.scala-lang.org") }
Scala 的 finally 语句不管 try 块是否抛出异常,都会执行,所以可以在 finally 块中添加代码以关闭已经打开的文件。
(19)match
Scala 的 match 表达式支持从多个选择中选取其一,类似 Java 中的 switch 语句。
val firstArg = if (args.length >0 ) args(0) else "" firstArg match { case "salt" => println("pepper") case "chips" => println("salsa") case "eggs" => println("bacon") case _ => println("huh?") }
上述代码和 Java 的 switch 相比有几点不同:
A、任何类型的常量都可以用在 case 语句中
B、case 语句无需使用 break
C、缺省匹配为”_”,其作用类似 java 中的 default
scala 的 match 表达式可以有返回值,如下:
val firstArg = if (args.length >0 ) args(0) else "" val friend = firstArg match { case "salt" => "pepper" case "chips" => "salsa" case "eggs" => "bacon" case _ => "huh?" }
(20)
脚本必须以结果表达式结束
(21)Scala编译器
Scala的基本编译器:scalac
快速Scala编译器:fsc 停止fsc后台进程:fsc -shutdown