Scala学习笔记(2)

来源:互联网 发布:支付宝与淘宝解绑不了 编辑:程序博客网 时间:2024/05/21 12:46

Scala基本语言特性

相比JavaC++等语言,Scala融合了OOPFP等编程范式,同时语法上更灵活

语法基础(概览)

  • Scala语言中不强制要求分号,可以依行断句,只有一行带有多个语句时才要求分号隔开。
  • 使用varval定义变量常量,类型可以由编译器推导,也可以显式指定。定义变量时甚至可以省略varval关键字,无关键字时定义的变量默认即为val,在定义变量的同时就需要初始化变量,否则报错(抽象类中除外)。
  • 使用def关键字定义方法varval定义函数,需要注意的是使用var定义的函数是可以更改实现的,但def定义的方法一经定义实现就不可改变
  • 没有自增/自减操作符。
  • 所有类型皆为对象,基础类型如IntDouble等都是类,函数/方法返回值的空类型为Unit,相当于Java/C++中的void
  • 没有原生enum类型,应继承枚举助手类Enumeration
  • 不提供类似Java/C++中的三目运算符,但if语句表达式带有返回值,可以实现类似效果。
  • 访问类成员权限默认为public,因而没有public关键字,但有privateprotected关键字,作用与Java大致相同,但支持更细粒度的权限控制。
  • 可以使用操作符作为函数名,达到类似C++/C#中操作符重载的效果。
  • 类的成员变量可以与方法名称相同

Hello World

创建文件Test.scala,输入以下代码:

object Test {    def main(args: Array[String]): Unit     //带有等号的方法可以省略返回值类型由编译器进行推导        = println("Hello World!")}

与Java类似,Scala也是从主方法main中开始执行整个程序,不过main方法并不定义在类中,而是定义在单例对象中(使用object关键字创建单例对象)。
将主方法写在class中能够通过编译,但生成的字节码文件在执行时会出错。
也可以不手动定义main方法而去让伴生对象继承App特质,即可直接执行代码语句,例如:

object Test extends App {    println("Hello World!")}

单例对象的名称可以与源码文件的文件名不同。

方法(Method)

与Java不同,Scala中同时支持函数方法(Java只有方法而没有真正意义上的”函数”,只有与”函数”类似的”静态方法”)。
方法由def关键字定义,可以被def方法、val函数重写。一个典型的方法格式如下:

def methodName(args: Type):Type = {    /* function_body */}

Scala中方法体不需要显式使用return关键字来给出方法返回值,编译器会将函数体的最后一句代码推导出类型做为整个函数的返回值。
对于有返回值的方法,必须要在方法定义中加入等号,否则编译器不会推导返回值。
即使方法的返回值为Unit,只要显式指定了返回值类型,则必须在方法体中加入等号。
方法和函数的形参不需要不能使用valvar关键字声明,只需写明类型即可。
在Scala中,方法允许省略参数,空的参数表可以直接省略,如:

def getNum: Int = 100def getNum(): Int = 100                 //以上两个定义作用相同,但只能存在一个

无参方法与空参方法只能存在一个,但二者在使用方式上略有不同,无参方法在调用时只能直接使用方法名,在方法名后加上括号调用就会出错;但空参方法既可以使用带有括号的方法调用方式,也可以省略括号,例如:

scala> def getNum: Int = 100            //定义了方法 getNum: IntgetNum: Intscala> getNum                           //正确,返回 100res0: Int = 100scala> getNum()                         //错误,提示 error: Int does not take parameters<console>:12: error: Int does not take parameters    getNum()          ^scala> def getNum(): Int = 200          //定义了方法 getNum(): IntgetNum: ()Intscala> getNum                           //正确,返回 200res1: Int = 200scala> getNum()                         //正确,返回 200res2: Int = 200

同时,无参方法不能与已有字段名称相同(编译报错),而空参方法允许带有同名的字段。
需要注意的是,在Scala中,赋值表达式的值为Unit,而不是类似Java/C++中的以被赋值的变量类型为表达式的值。例如:

scala> var _num = 0_num: Int = 0scala> def testNum(num: Int): Int = _num = num      //由编译器推断出的返回值类型为Unit<console>:12: error: type mismatch;found   : Unitrequired: Int    def testNum(num: Int): Int = _num = num                                      ^

参数默认值

在Scala中,方法中参数允许带有默认值

scala> var num = 100num: Int = 100scala> def setNum(p: Int = 0) { num = p }       //方法的参数不能由默认值进行类型推导,即使给参数写明了默认值,也依然需要显式指明类型setNum: (p: Int)Unitscala> setNum()             //对于有参数的方法,即使参数带有默认值使得参数表可以为空但在调用时依然不能省略括号,否则报错scala> println(num)0                           //输出0

如果一个方法中包含多个同类型并带有默认值的参数,调用时默认匹配第一个参数:

scala> def func(num1: Int = 100, num2: Int = 200) = println(s"$num1 $num2")func: (num1: Int, num2: Int)Unitscala> func(300)300 200

具名参数

在Scala中,调用方法时可以在参数表中写明参数的名称,该特性被称为”具名参数”。
对于方法中包含多个同类型并带有默认值参数的情况下,使用该特性可以显式指定要传入的是哪一个参数:

scala> func(num2 = 300)100 300

与C++不同,Scala中,方法参数的默认值不需要连续,参数的默认值可以交错出现,甚至是颠倒参数顺序:

scala> def func(int: Int, str: String = "String", char: Char, double: Double = 123.0) = println(s"$int $str $char $double")func: (int: Int, str: String, char: Char, double: Double)Unitscala> func(100, 'c')<console>:12: error: not enough arguments for method func: (int: Int, str: String, char: Char, double: Double)Unit.Unspecified value parameter char.        func(100, 'c')            ^scala> func(int = 100, char = 'c')          //对于默认参数不连续的方法,需要使用"具名参数"100 String c 123.0

默认参数与方法重载

在Scala中,若一个带有默认的参数的方法省略默认参数时签名与一个已经存在的方法相同,编译器并不会报错(C++编译器则会报错),而是在调用方法时优先使用无默认值的版本(处理逻辑类似于C#):

object Main extends App {    def func() = println("No Args")    def func(num: Int = 100) = println(num)    func()}

输出结果:
No Args

函数(Function)

在Scala中函数使用var``val关键字定义,即函数是一个存储了函数对象的字段。
一个典型的函数定义如下:

var functionName: FuncType = 符合签名的方法/函数/Lambda

Scala中的函数类型为Function,根据参数数目的不同,Scala中提供了Function0[+R](无参数)到Function22[-T1, ..., -T22, +R]共23种函数类型,即Scala中的函数,最多可以拥有22个参数。
与方法不同,函数不能够带有默认值。
需要注意的是,函数不允许省略参数,因为函数名做为表达式时的语义为函数名所代表的函数内容而非函数调用。空参函数的括号不可省略,直接使用函数名并不代表调用空参函数,比如:

scala> var show100: () => Int = () => 100show100: () => Int = <function0>scala> show100              //直接使用函数名得到的是函数对象而非调用函数res0: () => Int = <function0>scala> show100()res1: Int = 100

在Scala中,可以直接使用Lambda创建匿名函数后立即使用:

scala> ((str: String) => println(str))("Hello World!")Hello World!

C++中的Lambda用法类似:

#include <iostream>using namespace std;int main(void){    [](const string& str) { cout << str << endl; } ("Hello World!");    return 0;}

然而在C#中,Lambda需要创建对象或显式指明类型才能使用,同样的语句需要写成:

using System;class Test{    static void Main(string[] args)        => new Action<string>(str => Console.WriteLine(str))("Hello World!");        //或者写成 ((Action<string>)(str => Console.WriteLine(str)))("Hello World!");}

函数组合

在Scala中,函数允许进行组合。
函数组合有两种方式,a compose b实际调用次序为a(b())a andThen b实际调用次序为b(a())
需要注意的是,方法不能直接进行组合,需要将其转化为函数(方法名之后加_符号)。

object Main extends App {    def add(num: Int) = num + 100    def double(num: Int) = num * 2    //只有函数能进行组合,方法需要加"_"符号转化成函数    var compose = add _ compose double    var andThen = add _ andThen double    println(compose(100) == add(double(100)))    println(andThen(100) == double(add(100)))}

输出结果:
true
true

传名参数(By-name Parameter)

当一个方法接收的参数时,该参数即为传名参数(By-name Parameter),如下所示:

def func(arg: => T) ...

可以使用传名参数可以接收任意数量的代码,如下所示:

object Main extends App {    def show(args: => Unit) = args    //单行语句可直接作为参数    show(println("123"))    //多行语句可放在大括号中    show {        println("456")        println("789")    }}

运行结果:
123
456
789

函数作为参数

Scala为函数式编程语言,在Scala中函数对象可以直接作为参数传递。
当函数作为参数存在时,传名参数与普通的空参函数参数定义不能同时存在,如下定义只能存在一个:

def func(arg: () => T) = argdef func(arg: => T) = argvar func: (() => T) => T = (arg: () => T) => arg

在接收参数时,空参函数参数只能接收同样空参的函数,即() =>不能被省略,而传名参数则无此限制。

类型系统

在Scala中,所有的类型皆为对象,所有类型都从根类Any继承,AnyAnyValAnyRef两个子类。
在Scala中,基础类型如IntFloatDoubleUnit等全部从AnyVal类中派生,因而可以直接在泛型中直接使用这些类作为类型参数。
同时,Scala中提供了隐式转换(ImplicitConversion)来保证Int``Float``Double等类型之间可以自动进行转换
基础类型与字符串(String)等类型之间的转换也由类提供的成员函数进行,如将数值与字符串相互转换可以使用如下代码:

var str = 100.toStringvar num = str.toInt

在Scala中,所有的基础类型之外的引用类型派生自类AnyRef

底类型(Bottom)

与Java不同,Scala中存在底类型(bottom)。底类型包括NothingNull

  • Nothing是所有类型Any的子类型,定义为final trait Nothing extends Any
  • Nothing特质没有实例。
  • Null是所有引用类型AnyRef的子类型,定义为final trait Null extends AnyRef
  • Null特质拥有唯一实例null(类似于Java中null的作用)。

可空类型

在Scala中,使用Option[T]表示可空类型,Option[T]包含两个子类,Some[T]None,分别代表值存在/值不存在。
Option[T]类型使用getOrElse()方法来获取存在的值或是当值不存在时使用指定的值,如下所示:

scala> var str1: Option[String] = "test"<console>:10: error: type mismatch;         //赋值失败,Option[T]只能接收Option及其子类found   : String("test")required: Option[String]    var str1: Option[String] = "test"                               ^scala> var str1: Option[String] = Option("test")str1: Option[String] = Some(test)scala> var str2: Option[String] = Nonestr2: Option[String] = Nonescala> println(str1 getOrElse "Get Value Failed!")testscala> println(str2 getOrElse "Get Value Failed!")Get Value Failed!                           //输出getOrElse()方法中设定的值

可空类型也可以用于模式匹配中,如下代码所示:

var str = 100.toStringvar num = str.toInt

在Scala中,所有的基础类型之外的引用类型派生自类AnyRef

底类型(Bottom)

与Java不同,Scala中存在底类型(bottom)。底类型包括NothingNull

  • Nothing是所有类型Any的子类型,定义为final trait Nothing extends Any
  • Nothing特质没有实例。
  • Null是所有引用类型AnyRef的子类型,定义为final trait Null extends AnyRef
  • Null特质拥有唯一实例null(类似于Java中null的作用)。

可空类型

在Scala中,使用Option[T]表示可空类型,Option[T]包含两个子类,Some[T]None,分别代表值存在/值不存在。
Option[T]类型使用getOrElse()方法来获取存在的值或是当值不存在时使用指定的值,如下所示:

scala> var str1: Option[String] = "test"<console>:10: error: type mismatch;         //赋值失败,Option[T]只能接收Option及其子类found   : String("test")required: Option[String]    var str1: Option[String] = "test"                               ^scala> var str1: Option[String] = Option("test")str1: Option[String] = Some(test)scala> var str2: Option[String] = Nonestr2: Option[String] = Nonescala> println(str1 getOrElse "Get Value Failed!")testscala> println(str2 getOrElse "Get Value Failed!")Get Value Failed!                           //输出getOrElse()方法中设定的值

可空类型也可以用于模式匹配中,如下代码所示:

object TestOption extends App {    var s  = List(Some(123), None)    for (num <- s)        num match {            case Some(x) => println(x)            case None => println("No Value")        }}

运行结果:
123
No Value

0 0
原创粉丝点击