5.4 Scala语法和规则

Scala不像Java那样严格而繁琐:分号是可选的;在不必要的情况下,函数调用中的小括号也并非必不可少的(请看前述示例中的代码行val b = new AddDemoFunctional)。本节介绍如下主题:

  • 静态类型语言;
  • 可修改的变量和不可修改的变量;
  • 常用的Scala类型。

5.4.1 静态类型语言

与Java一样,Scala也是一种静态类型语言:使用变量前必须先声明。Scala也是一种强类型语言:你总是可以指定要使用的类型,就像在Java代码中一样;但与Java不同的是,并非必须显式地指定类型。

声明方法的输入参数和返回值时,必须指定类型,但在方法或函数中声明变量时,并非必须指定类型,因为Scala编译器通常能够根据代码推断出变量的正确类型。

下面是一个这样的示例:

  1. var i = 10;
  2. var j = new java.lang.Object();

声明可修改的变量后,就可使用它来存储指定类型或可向上转换为该类型的值。例如,可在上述代码后面编写如下代码:

  1. j = "Hello world"

由于String类型可向上转换为java.lang.Object,因此可使用变量j来存储指向字符串"Hello world"的引用。但不能将String赋给变量i,因为你将一个Int实例赋给了这个变量,String不能向上转换为Int实例。

可显式地指定变量的类型;使用类系列时,可能必须提供这种信息:

  1. val i: Integer = 10

5.4.2 可修改的变量和不可修改的变量

Scala支持两种变量。声明方法的参数或类的实例成员时,必须使用下面两个关键字之一。

  • var:用于声明可修改的变量;
  • val:用于声明固定变量。

可修改的变量相当于Java中的普通变量,它们是可修改的,可随便修改;固定变量相当于Java中的final变量,只能给它们赋值一次。如果固定变量指向一个可修改的类实例,你依然可以修改这个实例的内容,这在第2章讨论过。

5.4 Scala语法和规则 - 图1 进行函数式编程时,应尽可能使用不可修改的变量。不可修改的值是函数式编程的基石。

Scala不支持静态变量(也叫类变量),但正如后面将介绍的,它支持可用于替代静态变量的单例对象。

5.4.3 常用的Scala类型

在Scala中,可使用常见的Java类,但Scala也提供了自己的类,你应尽可能使用这些类。这些类的工作原理带有Scala的烙印,这里介绍其中的如下几个:

  • Any
  • AnyRef
  • AnyVal
  • 字符串。

  • Any类

在Java中,祖先类为Object,而在Scala中,祖先类为Any,因此在没有显式指定时,所有的类都隐式地继承Any类。

5.4 Scala语法和规则 - 图2

如上图所示,Any类有两个子类:

  • AnyRef
  • AnyVal
    (1) AnyRef类——引用类

AnyRef类由引用变量使用,类似于Java类java.lang.Object,提供的方法也类似,如equals()hashCode()finalize()。Scala中的大部分类都直接或间接地继承了AnyRef类。

(2) AnyVal类——值类

不同于Java,Scala是一种纯粹的面向对象语言,因此不支持基本类型值,而是将它们封装在包装类中:

5.4 Scala语法和规则 - 图3

这些包装类都是AnyVal的子类,它们被称为值类,Scala编译器以特殊的方式处理它们。

你可能会问,Scala为何使用自己的包装类,而不使用Java类库中的包装类。一个原因是Scala力图通过避免不必要的装箱来改善性能,为此需要有自己的内部逻辑;另一个重要的原因是Scala支持运算符重载(这不同于Java)。Scala包装类实现了用于计算的所有二元运算符,稍后将更详细地讨论这一点。

  • 字符串

很多JVM语言都提供了自己的字符串类,这些类除了Java标准类String的方法和字段外,还提供了其他的便利方法和字段,但Scala不是这样的,它使用Java类库中java.lang包中的String类。

Java字符串是不可修改的。如果方法要修改字符串,它将返回一个新的String实例,其中包含修改后的字符串,而原来的String实例保持不变。这种不变性正是Scala的函数式编程功能所需要的。