购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

1.3 Scala基础

建议读者在最初学习Scala的时候,在Scala命令行模式中操作,最终程序的编写可以在IDE中进行。在Windows的CMD窗口中或CentOS的Shell命令中执行scala命令,即可进入Scala的命令行操作模式。

本节将在Scala的命令行操作模式中讲解Scala的基础知识。

1.3.1 变量声明

Scala中变量的声明使用关键字val和var。val类似Java中的final变量,也就是常量,一旦初始化则不可修改;var类似Java中的非final变量,可以被多次赋值,多次修改。

例如,声明一个val字符串变量str:

    scala> val str="hello scala"
    str: String = hello scala

上述代码中的第二行为执行第一行的输出信息,从输出信息中可以看出,该变量在Scala中的类型是String。

当然也可以在声明变量时指定数据类型,与Java不同的是,数据类型需要放到变量名的后面,这使得读者面对复杂的数据类型时更易阅读:

    scala> val str:String="hello scala"
    str: String = hello scala

由于val声明的变量是不可修改的,若对上方声明的变量str进行修改,则会报以下错误:

    scala> str="hello scala2"
    <console>:12: error: reassignment to val
          str="hello scala2"
             ^

因此,如果希望变量可以被修改,就需要使用var声明:

    scala> var str="my scala"
    str: String = my scala
       
    scala> str="my scala2"
    str: String = my scala2

如果需要换行输入语句,那么只需要在换行的地方按回车键,解析器会自动在下一行以竖线进行分割:

    scala> val str=
        | "hello everyone"
    str: String = hello everyone

此外,还可以将多个变量放在一起进行声明:

    scala> val x,y="hello scala"
    x: String = hello scala
    y: String = hello scala

Scala变量的声明,需要注意的地方总结如下:

· 定义变量需要初始化,否则会报错。

· 定义变量时可以不指定数据类型,系统会根据初始化值推断变量的类型。

· Scala中鼓励优先使用val(常量),除非确实需要对其进行修改。

· Scala语句不需要写结束符,只有同一行代码使用多条语句时才需要使用分号隔开。

1.3.2 数据类型

在Scala中,所有的值都有一个类型,包括数值和函数。Scala的类型层次结构,如图1-4所示。

图1-4 Scala的类型层次结构

Any是Scala类层次结构的根,也称为超类或顶级类。Scala执行环境中的每个类都直接或间接地从该类继承。该类中定义了一些通用的方法,例如equals()、hashCode()和toString()。Any有两个直接子类:AnyVal和AnyRef。

AnyVal表示值类型,有9种预定义的值类型,分别是非空的Double、Float、Long、Int、Short、Byte、Char、Unit和Boolean。Unit是一个不包含任何信息的值类型,和Java语言中的Void等同,用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()。

AnyRef表示引用类型,所有非值类型都被定义为引用类型。Scala中的每个用户定义类型都是AnyRef的子类型,AnyRef对应于Java中的Java.lang.Object。

例如,下面的例子定义了一个类型为List[Any]的变量list,list中包括字符串、整数、字符、布尔值和函数,由于这些元素都属于对象Any的实例,因此可以将它们添加到list中,代码如下:

    val list: List[Any] = List(
      "a string",
      732,  //an integer
      'c',  //a character
      true, //a boolean value
      () => "an anonymous function returning a string"
    )
       
    list.foreach(element => println(element))

上述代码的输出结果如下:

    a string
    732
    c
    true
    <function>

Scala中的值类型可以按照图1-5的方式转换,且转换是单向的。

图1-5 Scala值类型转换

例如下面的例子,允许将Long型转换为Float型,Char型转换为Int型:

下面的转换是不允许的:

    val x: Long = 987654321
    val y: Float = x  //9.8765434E8
    val z: Long = y  //不符合

此外,Scala还可以将引用类型转换为其子类型。

Nothing是所有类型的子类,在Scala类层级的最低端。Nothing没有对象,因此没有具体值,但是可以用来定义一个空类型,类似于Java中的标示性接口(如Serializable,用来标识该类可以进行序列化)。举个例子,如果一个方法抛出异常,异常的返回值类型就是Nothing(虽然不会返回)。

Null是所有引用类型的子类,所以Null可以赋值给所有的引用类型,但不能赋值给值类型,这个和Java的语义是相同的。Null有一个唯一的单例值null。

1.3.3 表达式

Scala中常用的表达式主要有两种:条件表达式和块表达式。

1.条件表达式

条件表达式主要是含有if/else的语句块,如以下代码所示:

    scala> val i=1
    i: Int = 1
       
    scala> val result=if(i>0) 100 else -100
    result: Int = 100

由于if和else的返回结果同为Int类型,因此变量result为Int类型。

若if与else的返回类型不一致,则变量result为Any类型,如下示例:

    scala> val result=if(i>0) 100 else "hello"
    result: Any = 100

当然也可以在一个表达式中进行多次判断,例如:

    scala> val result=if(i>0) 100 else if(i==0) 50 else 10
    result: Int = 100

2.块表达式

块表达式为包含在符号“{}”中的语句块,例如以下代码:

    scala> val result={
         | val a=10
         | val b=10
         | a+b
         | }
    result: Int = 20

代码中的竖线表示Scala命令行中的换行,在实际程序中不需要编写。

需要注意的是,Scala中的返回值是最后一条语句的执行结果,而不需要像Java一样单独写return关键字。

如果表达式中没有执行结果,就返回一个Unit对象,类似Java中的void。例如以下代码:

    scala> val result={
         | val a=10
         | }
    result: Unit = ()

1.3.4 循环

Scala中的循环主要有三种:for循环、while循环和do while循环。

1. for循环

for循环的语法如下:

    for(变量<-集合或数组){
      方法体
    }

表示将集合或数组中的每一个值循环赋给一个变量。

例如,循环从1到5输出变量i的值,代码如下:

    scala> for(i<- 1 to 5) println(i)
    1
    2
    3
    4
    5

1 to 5表示将1到5的所有值组成一个集合,且包括5。若不想包括5,则可使用关键字until,代码如下:

    scala> for(i<- 1 until 5) println(i)
    1
    2
    3
    4

使用这种方式可以循环输出字符串。例如,将字符串“hello”中的字符循环输出,代码如下:

    scala> val str="hello"
    str: String = hello
       
    scala> for(i<-0 until str.length) println(str(i))
    h
    e
    l
    l
    o

还可以将字符串看作一个由多个字符组成的集合,因此上面的for循环写法可以简化,代码如下:

    scala> for(i<-str) println(i)
    h
    e
    l
    l
    o

Scala的嵌套循环比较简洁,例如以下代码,外层循环为i<-1 to 3,内层循环为j<-1 to 3 if (i!=j),中间使用分号隔开:

    scala> for(i<-1 to 3;j<-1 to 3 if (i!=j)) println(j)
    2
    3
    1
    3
    1
    2

上述代码等同于以下Java代码:

    for (int i = 1; i <= 3; i++) {
      for (int j = 1; j <= 3; j++) {
       if(i!=j){
         System.out.println(j);
       }
      }
    }

2. while循环

Scala的while循环与Java类似,语法如下:

    while(条件)
    {
       循环体
    }

例如以下代码:

    scala> var i=1
    i: Int = 1
       
    scala> while(i<5){
        |  i=i+1
        |  println(i)
        | }
    2
    3
    4
    5

3. do while循环

与Java语言一样,do while循环与while循环类似,但是do while循环会确保至少执行一次循环。语法如下:

    do {
       循环体
    } while(条件)

例如以下代码:

    scala> do{
         |    i=i+1
         |    println(i)
         | }while(i<5)
    2
    3
    4
    5

1.3.5 方法与函数

Scala中有方法与函数。Scala方法是类或对象中定义的成员,而函数是一个对象,可以将函数赋值给一个变量。换句话说,方法是函数的特殊形式。

1.方法

Scala中的方法跟Java的类似,方法是组成类的一部分。Scala中方法的定义使用def关键字,语法如下:

    def 方法名 (参数列表):返回类型={
       方法体
    }

例如以下代码,将两个数字求和然后返回,返回类型为Int:

    def addNum( a:Int, b:Int ) : Int = {
          var sum = 0
          sum = a + b
       return sum
    }

只要方法不是递归的,可以省略返回类型,系统会自动推断返回类型,并且返回值默认是方法体的最后一行表达式的值。当然也可以用return来执行返回值,但Scala并不推荐。

因此,可以对上面的代码进行简写,去掉返回类型和return关键字,代码如下:

    def addNum( a:Int, b:Int ) = {
         var sum = 0
         sum = a + b
         sum
    }

方法的调用与Java相同,代码如下:

    addNum(1,2)

如果方法没有返回结果,那么可以将返回类型设置为Unit,类似Java中的void,代码如下:

    def addNum( a:Int, b:Int ) : Unit = {
         var sum = 0
         sum = a + b
         println(sum)
    }

在定义方法参数时,可以为某个参数指定默认值,在方法被调用时可以不为带有默认值的参数传入实参。例如以下方法,指定参数a的默认值为5:

    def addNum( a:Int=5, b:Int ) = {
         var sum = 0
         sum = a + b
         sum
    }

上述方法可以用如下代码调用,通过指定参数名称,只传入参数b:

    addNum(b=2)

也可以将a、b两个参数都传入:

    addNum(1,2)

需要注意的是,当未被指定默认值的参数不是第一个时,参数名称不能省略。例如下面的调用是错误的:

    addNum(2)//错误调用

当方法需要多个相同类型的参数时,可以指定最后一个参数为可变长度的参数列表,只需要在最后一个参数的类型之后加一个星号即可。例如以下方法,参数b可以是0到多个Int类型的参数:

    def addData( a:String,b:Int* ) = {
        var res=0
         for (i <- b)
           res=res+i
         a+res
    }

在方法内部,重复参数的类型实际上是一个数组。因此,上述方法中的参数b的类型实际上是一个Int类型的数组,即Array[Int]。可以使用如下代码对上述方法进行调用:

    val res=addData("hello",3,4,5)
    println(res)

输出结果为:hello12。

但是如果直接向方法addData()传入一个Int类型的数组,编译器反而会报错:

    val arr=Array(3,4,5)
    val res=addData("hello",arr)//此写法不正确
    println(res)

此时需要在数组参数后添加一个冒号和一个_*符号,以告诉编译器分别把数组arr的每个元素当作参数,而不是将数组当作单一的参数传入,代码如下:

    val arr=Array(3,4,5)
    val res=addData("hello",arr:_*)
    println(res)

输出结果同样为:hello12。

2.函数

函数的定义与方法不一样,语法如下:

    (参数列表)=>函数体

例如以下代码定义了一个匿名函数,参数为a和b,且都是Int类型,函数体为a+b,返回类型由系统自动推断,推断方式与方法相同:

    ( a:Int, b:Int ) =>a+b

如果函数体有多行,就可以将函数体放入一对{}中,并且可以通过一个变量来引用函数,变量相当于函数名称,代码如下:

    val f1=( a:Int, b:Int ) =>{ a+b }

此时可以通过如下代码对其进行调用:

    f1(1,2)

当然,函数也可以没有参数,代码如下:

    val f2=( ) =>println("hello scala")

此时可以通过如下代码对其进行调用:

    f2()

3.方法与函数的区别

方法与函数的区别有以下三点:

(1)方法是类的一部分,而函数是一个对象并且可以赋值给一个变量。

(2)函数可以作为参数传入方法中。

例如,定义一个方法m1,参数f要求是一个函数,该函数有两个Int类型的参数,且函数的返回类型为Int,在方法体中直接调用该函数,代码如下:

    def m1(f: (Int, Int) => Int): Int = {
      f(2, 6)
     }

定义一个函数f1,代码如下:

    val f1 = (x: Int, y: Int) => x + y

调用方法m1,并传入函数f1:

    val res = m1(f1)
    println(res)

此时输出结果为8。

(3)方法可以转换为函数。当把一个方法作为参数传递给其他的方法或者函数时,系统会自动将该方法转换为函数。

例如,有一个方法m2,代码如下:

    def m2(x:Int,y:Int) = x+y

调用上面的m1方法,并将m2作为参数传入,此时系统会自动将m2方法转为函数:

    val res = m1(m2)
    println(res)

此时输出结果为8。

除了系统自动转换外,也可以手动进行转换。在方法名称后加一个空格和一个下划线,即可将方法转换为函数。代码如下:

    //将方法m2转换为函数
    val f2=m2 _
    val res=m1(f2)
    println(res)

此时输出结果仍然为8。 kHQN6tD/7etoBTS695PmqTeuUlLHuCEJUudSgWZiCDiVIknkImwtFh5R9vHwoJuM

点击中间区域
呼出菜单
上一章
目录
下一章
×