转载请注明joymufeng,欢迎访问 PlayScala社区(http://www.playscala.cn)
原文链接:http://www.playscala.cn/doc/catalog?_id=j1_14
2.1 运行Scala代码
感谢Scala.js项目,我们可以在浏览器中运行Scala代码。点击ScalaFiddle开始我们的Scala之旅吧!
2.2 Scala的特性
每一种编程语言的存在都有意义,Scala存在的意义是为了让那些热爱编程的人更加热爱编程。Scala的设计简洁而优雅,很多地方都彰显了编程语言的一致性,例如在Scala的世界里:
一切都是对象
你可以直接调用基本类型上的方法:
1.toDouble // 1.0 10.toHexString // a "1".toInt // 1
一切都是方法
在Scala中其实没有+-*/这些运算符,它们其实是基本类型上的方法。例如String类型上的*方法用于将当前字符串重复n次并拼接为一个新的字符串:
"a".*(3) // "aaa"
但是这种写法可读性很糟糕,如果方法只包含一个参数,那么你可以省略"."和"()",写成:
"a" * 3 // "aaa"
这样看起来是不是舒服多了。不止是String类型,你可以重新定义任何类型上的*方法。
在调用Scala对象上的方法时,变量名和方法名中间的点"."可以省略,进一步,如果方法只包含一个参数,则参数两边的括号"()"可以省略。在后面我们会发现,利用这两个语法糖,我们可以自定义一些特殊语法,并且让它们看起来像是编程语言提供的功能。
一切都是表达式
任何语句都会一个返回值,编译器会自动帮你推断返回值类型:
val i = if(true){ 1 } else { 0 } // i = 1
Scala拥有一套强大的类型推导系统,你可以像动态类型语言那样编码,大大降低了代码的冗余度,同时也增强了代码的可读性。
很多人在刚开始学习Scala时就被一些奇怪的符号吓到,其实当你明白其背后的设计用意后,你不但不会觉得它可怕,反而会觉得有点可爱。举个例子,很多人看到Scala使用::拼接元素,使用:::拼接列表,就像下面这样:
val list1 = List("c", "a", "l", "a") val list2 = s :: list1 // list2: (s, c, a, l, a) val list3 = List("p", "l", "a", "y") val list4 = list3 ::: list2 // list4: (p, l, a, y, s, c, a, l, a)
我们之前说过在Scala里一切都是方法,所以::和:::自然也是方法,只不过是为了简洁,省略了.和()。在Scala中列表List被设计成由head和tail拼接在一起的递归结构(这种设计在模式匹配时非常有用), List的定义可以写成如下形式:
head :: tail
head是首元素,tail是剩余的List。仔细瞧瞧::看起来是不是很像胶水,将列表的头和尾紧紧地粘在一起,更进一步:::可以把两个列表粘在一起。这样的代码是不是很简洁,并且富有表达力呢! 在Scala中,类似这样的设计比比皆是,例如我们再来看看如何构建一个Map实例:
val map = Map("name" -> "PlayScala社区", "url" -> "http://www.playscala.cn")
感受一下,是不是非常清晰明了。 当然Scala的魅力远不止如此,当你慢慢了解它时,你会慢慢深陷而无法自拔。
2.3 Hello, Scala
让我们从经典的Hello, World开始:
package txt object Hello { def main(args: Array[String]): Unit = { println("Hello, Scala") } }
在Scala中,程序的入口是object对象,object对象无须实例化可以直接运行,你可以认为它是Java的单例对象。执行的入口方法是main函数,参数是一个字符串数组,main方法的返回类型是Unit,Unit表示一个无值类型,类似于Java的void方法。println方法用于向控制台输出内容。
Scala的泛型类型使用"[]"而不是像Java那样使用"<>",因为在Scala中"<"和">"是有效的方法名,它们有更重要的用途。
2.4 变量声明
val用于定义不可变变量,var用于定义可变变量,这里的"可变"指的是引用的可变性。val定义的变量类似于Java的final变量,即变量只能赋一次值:
val msg = "hello" // 等价于:val msg: String = "hello" var i = 1 // 等价于:var i: Int = 1 i = i + 1
为了方便阅读,定义变量时也可以显式指定其类型:
val msg: String = "hello" var i: Int = 1
因为变量类型是可忽略的,所以放在了变量名后面。后面我们会发现Scala的类型信息都放在后面,采用类型后置语法。
变量后面的类型声明可以省略,每行代码末尾的分号";"也可以省略。
2.5 函数声明
函数支持是Scala语言的最大亮点,相对于Java的Lambda和函数式接口,你可以享受到原生的函数式编程。关键字def用于定义函数:
def max(x: Int, y: Int): Int = { if (x > y) { x } else { y } } val maxVal = max(1, 2) // 2
Scala不建议在函数体内使用return语句,因为过多的return会使得代码逻辑混乱。Scala默认使用函数体的最后一个表达式作为返回值。当然你仍然可以使用return语句指定返回值。
你可以像基本类型那样把函数赋给一个变量:
val max = (x: Int, y: Int) => { if (x > y) { x } else { y } } val maxVal = max(1, 2) // 2
等号"="右边是一个匿名函数,也就是我们常说的Lambda函数,匿名函数由参数和函数体两部分组成,中间用"=>"隔开,这里省略了max变量的类型,因为编译器可以自动推断出来,完整的写法如下:
val max: (Int, Int) => Int = (x: Int, y: Int) => { if (x > y) { x } else { y } }
max的类型是(Int, Int) => Int
,即接受两个Int参数,产生一个Int返回值的函数类型。
2.6 控制结构
if语法结构和Java很像,区别是Scala的if是表达式,可以返回一个值:
val i = if(true){ 1 } else { 0 } // i = 1
while循环的语法如下:
while (n > 0) { println(n) n -= 1 }
for语法结构为:
for (i <- 1 to 10) { println(i) }
1 to 10等价于1.to(10),返回包含数字1到10的Range类型。
2.7 class
Scala的class定义和Java很相似:
class Counter { private var value = 0 def increment() { value += 1} //方法可见性默认public def current() = value }
类成员(属性和方法)的可见性默认为public。
Scala的源文件中可以定义多个类,并且默认都是public,所以外界都可以看见。class的使用也很简单:
val myCounter = new Counter //或new Counter() myCounter.increment() println(myCounter.current) //或myCounter.current()
Scala中如果对象方法或类的构造器没有参数,则括号"()"可以省略。
Scala会为所有的属性生成相应可见性的setter和getter方法,例如:
class Person{ var age = 0 }
编译器会自动为age属性生成setter和getter方法,方法名分别为age_=和age:
println(p.age) // 将会调用方法p.age() p.age = 30 // 将会调用方法p.age=(30)
在Scala中,setter/getter方法名未采用setXxx/getXxx格式,如果需要Java风格的setXxx/getXxx格式可以使用@BeanProperty注解。
当然你也可以重新定义setter和getter方法:
class Person{ private var privateAge = 0 def age = privateAge def age_=(newValue: Int) { privateAge = newValue } }
2.8 object
Scala没有静态方法和静态字段,而是提供了object对象,也就是Java中的单例对象,即全局只有一个实例:
object Accounts { private var lastNumber = 0 def newUniqueNumber() = { lastNumber += 1; lastNumber } }
因为Accounts是一个单例对象,可以直接使用而无需初始化:
val uniqueNumber = Accounts.newUniqueNumber
object的另一个用法是作为类的伴生对象, 类似于Java类上的静态方法,只不过Scala将Java类上的静态功能全交给object实现。object作为伴生对象时必须和类在同一个源文件中定义,并且可以相互访问私有属性。
2.9 apply方法
如果某个对象obj上定义了apply方法,则我们可以这样调用:
obj(arg1, ... , argn)
是的,你猜对了,伴生对象上的apply方法立马就派上用场了,例如List类有一个同名的伴生对象List,那么你可以这样初始化一个列表:
val list = List("a", "b", "c")
想想下面的Java版本,是不是感觉幸福感油然而生:
List<String> list = new ArrayList<String>(); list.add("a"); list.add("b"); list.add("c");
2.10 块表达式
在Scala中一切都是表达式,如果表达式含有多条语句,则使用大括号"{}"括起来,形成一个块表达式,块表达式的最后一条语句的值作为整个块的返回值。
val r = { val i = 1 val j = 2 i + j } // r = 3