Play For Scala 开发指南 - 第2章 Scala基本语法

原创
2017/10/13 16:58
阅读数 296
转载请注明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
展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
1 收藏
0
分享
返回顶部
顶部