一小时体验Scala
浏览了一遍官方的介绍性文档,整理此文以飨大家。接下来,我们将要介绍Scala的基本使用,只是介绍其基本使用。
该教程可以做为你如厕时打发时间来用,因为这篇文章足够短。大篇幅的文章会挫败人的学习热情,好的文章读起来就像拉大便一样,能一口气很流畅地搞完,才会让人爽。所以,这必然又是一篇"入厕文章",还是那句话,我希望本文能够让大家利用上下班,上厕所大便的时间学习一个技术。呵呵。
相信你现在已经在厕所里脱掉裤子露出屁股已经准备好大便了,那就让我们畅快地排泄吧……
[toc]
Scala介绍
Scala是一个现代的多范式编程语言。
Scala的特点:
简洁
优雅
类型安全
- 面向对象及函数式编程语言
Scala是一款纯面向对象的编程语言,也就意味着任何值都是对象。
Scala同时也是一款函数式编程语言,那也就意味着任何一个函数都是一个值。Scala支持匿名函数及高阶函数,允许函数嵌套。
Scala安装
Mac系统安装
Mac下安装非常简单,从Scala官网下载二进制包,然后解压到某个路径下,设置环境变量就搞定了。比如把Scala安装到/usr/local/share/scala下,如下:
wget https://downloads.lightbend.com/scala/2.12.2/scala-2.12.2.tgztar -xf scala-2.12.2.tgzmv scala-2.12.2 /usr/local/share/scala
设置环境变量:
export SCALA_HOME=/usr/local/share/scalaPATH="/Library/Frameworks/Python.framework/Versions/3.5/bin:${GOPATH}/bin:${SCALA_HOME}/bin:${PATH}"export PATH
重新加载.bash_profile文件:
. ~/.bash_profile
验证scala是否可用:
LavenLius-MacPro:scala liuchuan$ scalaWelcome to Scala 2.12.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_91).Type in expressions for evaluation. Or try :help.scala>
至此,Scala安装完毕。
Gnu/Linux安装
安装过程略。设置环境变量即可使用。
# /etc/profileexport JAVA_HOME=/usr/local/jdkexport SCALA_HOME=/usr/local/share/scalaexport PATH=$PATH:$JAVA_HOME/bin:$SCALA_HOME/bin
第一个Scala程序
一般情况下,编程语言都会从一个"Hello,World!"程序着手,我们也从一个"Hello,World!"开始,看看Scala的程序是如何写的。在Scala的交互式环境及写成文件的形式都是可以的。
LavenLius-MacPro:scala liuchuan$ cat HelloWorld.scala object HelloWorld { def main(args: Array[String]): Unit = { println("Hello, Scala!") }}
编译并运行。scalac
命令可以编译一个或多个Scala源文件并生成可以被任何标准的JVM虚拟机执行的Java字节码。Scala的编译器类似Java的编译器javac
。默认情况下,scalac
会在当前目录下生成class文件。我们可以使用-d
选项,指定class文件位置。
# 在当前目录生成class文件LavenLius-MacPro:scala liuchuan$ scalac HelloWorld.scala # 指定生成class文件的目录LavenLius-MacPro:scala liuchuan$ scalac -d classes HelloWorld.scala LavenLius-MacPro:scala liuchuan$ scala -classpath . HelloWorldHello, Scala!# cp选项是classpath的别名LavenLius-MacPro:scala liuchuan$ scala -cp . HelloWorldHello, Scala!
如果不编译文件,可以以脚本的形式来执行:
[lavenliu@mybox scala]$ scala HelloWorld.scalaHello, Scala!
表达式
表达式就是可以被计算的语句,如:
1 + 1
我们可以使用println
来打印表达式的结果:
scala> println(1)1scala> println(1 + 1)2scala> println("Hello!")Hello!scala> println("Hello" + "world!")Helloworld!
值
我们可以使用val
关键字给表达式命名,val
有点final
的意思,一旦赋值不能再次更改。很像ES6中的let
关键字。
scala> val x = 1 + 1x: Int = 2scala> println(x)2
像上面的为表达式的结果命名,如x,我们称之为values
。给值重新赋值,将会发生编译错误,也就是说,values不能重新赋值。
scala> val x = 1 + 1x: Int = 2scala> x = 3:12: error: reassignment to val x = 3 ^
values的类型可以被自动推导出来,就像上面的x: Int = 2
一样,不过我们可以明确指定值的类型,就像下面这样:
scala> val x: Int = 1 + 1x: Int = 2
注意到,类型Int
跟在值x
后面,并且值x
后面的:
不能省略。
与其他语言对比,类似JavaScript中的const
定义的常量。
变量
变量跟值类似,只不过变量可以重新赋值。我们可以使用关键字var
定义变量,var
定义的变量是非final
的。
scala> var x = 1 + 1x: Int = 2scala> x = 3x: Int = 3scala> println(x * x)9
同样,我们也可以在定义变量时指定其类型:
scala> var x: Int = 1 + 1x: Int = 2
块
我们可以把一些语句组织在一个块中,使用{}
。我们称之为block
。语句块的结果就是最后一个表达式的结果(类似lisp语言中的let
函数)。
scala> println({ val x = 1 + 1 x + 1 })3# 上面的代码块返回值为3,是x+1的结果,x为2
条件控制
绝大部分的编程语言在条件控制这一块基本一致,如果有过一门编程经验的话,条件控制这块完全不是问题。
while
[lavenliu@mybox scala]$ cat while01.scala var i = 0while (i < 10) { println(i) i += 1}
执行:
[lavenliu@mybox scala]$ scala while01.scala 0123456789
foreach
[lavenliu@mybox scala]$ cat foreach.scala args.foreach((arg) => println(arg))
执行:
$ scala foreach.scala Scala is FunScalaisFun
for
$ cat for.scala for (arg <- args) println(arg)
执行:
$ scala for.scala Scala is FunScalaisFun
函数
函数可以理解为带参数的表达式,既然是表达式就可以把它赋值给变量。既然可以当做变量来用,那么这门语言是支持函数式编程的,又支持高阶函数。
我们可以定义匿名函数,是不是很像某个语言的箭头函数?是不是也很像Java 8的lambda表达式?
scala> (x: Int) => x + 1res20: Int => Int = $$Lambda$1245/1467853846@18ed9480
在=>
的左边是参数列表,右边是函数体,很有JavaScript的味道。
我们也可以定义命名函数:
scala> val addOne = (x: Int) => x + 1addOne: Int => Int = $$Lambda$1246/1214357269@790ea58fscala> addOne(3)res21: Int = 4
函数可有多个参数,
scala> val add = (x: Int, y: Int) => x + yadd: (Int, Int) => Int = $$Lambda$1257/368310735@7b8d1537scala> println(add(1, 2))3
函数也可以没有参数,
scala> val getTheAnswer = () => 42getTheAnswer: () => Int = $$Lambda$1259/608738578@28df2da8scala> println(getTheAnswer())42
方法
方法看起来及其行为与函数很像,不过它们之间也有细微之处。方法的定义是使用def
关键字,def
后面跟方法名称、参数列表、返回类型及方法体。
scala> def add(x: Int, y: Int): Int = x + yadd: (x: Int, y: Int)Intscala> println(add(2, 3))5
注意,方法的返回值声明是在参数列表后面跟一个冒号及类型。方法的参数要指定参数的类型,因为编译器不推断参数的类型,返回值也是同样要指定类型。方法定义中的=
也需要注意,在函数式编程语言中,方法的定义是一个表达式并且可以赋值给一个变量。
方法可以接收多个参数列表。
scala> def addTheMultiply(x: Int, y: Int)(multipliter: Int): Int = (x+y)*multipliteraddTheMultiply: (x: Int, y: Int)(multipliter: Int)Intscala> printprint printf printlnscala> println(addTheMultiply(1, 2)(3))9
来一个没有参数的方法:
scala> def name: String = System.getProperty("name")name: Stringscala> println("Hello, " + name + "!")Hello, null!
方法虽然与函数有些不同,不过到目前为止,我们可以认为方法与函数长得很像,只是很像。
方法可以有多行表达式。
scala> def getSquareString(input: Double): String = { val square = input * input square.toString }getSquareString: (input: Double)Stringscala> getSquareString(4.0)res32: String = 16.0
方法体中的最后一个表达式就是方法的返回值。(Scala没有return
关键字,因为它很少用到。这种情况在Lisp、Ruby、Julia中也很常见。)如果方法体中只有一行表达式,那么可以省略方法体上的大括号。
还可以定义一个无参数及无返回值的方法,如下:
scala> def greet() = println("hello, world!")greet(): Unit
如果一个方法返回Unit
类型,则表示该方法返回没有实际意义的值,类似其他语言中的void
返回类型。
类
我们可以使用class
关键字定义类,后面跟类的名称及构造参数。
scala> class Greeter(prefix: String, suffix: String) { def greet(name: String): Unit = println(prefix + name + suffix) }defined class Greeter
方法greet
的返回类型为Unit
,也就是说,该方法并不返回什么有意义的东东。Unit
其实跟C或Java中的void
类似。(A difference is that because every Scala expression must have some value, there is actually a singleton value of type Unit, written (). It carries no information.)
定义了一个类后,怎么使用呢?可以使用关键字new
来实例化。
scala> val greeter = new Greeter("Hello, ", "!")greeter: Greeter = Greeter@202e6fd0scala> greeter.greet def greet(name: String): Unitscala> greeter.greet("Scala developer")Hello, Scala developer!
Case Classes
Scala有一种特殊的类叫做"case
"类。默认情况下,case
类是不可变的,只可以比较其值。我们可以使用case class
关键字定义case类。
scala> case class Point(x: Int, y: Int)defined class Point
我们可以不使用new
关键字就可以实例化case类。
scala> val point = Point(1, 2)point: Point = Point(1,2)scala> val anotherPoint = Point(1, 2)anotherPoint: Point = Point(1,2)scala> val yetAnotherPoint = Point(2, 2)yetAnotherPoint: Point = Point(2,2)
case类通过值进行比较。
scala> if (point == anotherPoint) { println(point + " and " + anotherPoint + " are the same.") } else { println(point + " and " + anotherPoint + " are different.") }Point(1,2) and Point(1,2) are the same.scala> if (point == yetAnotherPoint) { println(point + " and " + yetAnotherPoint + " are the same.") } else { println(point + " and " + yetAnotherPoint + " are different.") }Point(1,2) and Point(2,2) are different.
case类暂时介绍到这里,后续会继续深入讲解。
Objects
Objects are single instances of their own definitions. You can think of them as singletons of their own classes.这句话可以简单理解为 Objects
是其自身的一个实例,相对于单例类。
我们可以使用关键字object
定义Objects。
scala> object IdFactory { private var counter = 0 def create(): Int = { counter += 1 counter } }defined object IdFactory
我们通过引用其名字就可以访问:
scala> val newId: Int = IdFactory.create()newId: Int = 1scala> println(newId)1scala> val newerId: Int = IdFactory.create()newerId: Int = 2 # 这里的值是2,而不是1,那就有点意思了scala> println(newerId)2
Traits
通过查字典才知道这个单词的有:特质、性状之意。根据其意思我们就可以推断,Scala中的Traits应该是接口相关的东东了。官方的解释是:"Traits are types containing certain fields and methods. Multiple traits can be combined."
You can define traits with trait
keyword.
scala> trait Greeter { def greet(name: String): Unit }defined trait Greeter
Traits 可以有默认的实现。
scala> trait Greeter { def greet(name: String): Unit = println("Hello, " + name + "!") }defined trait Greeter
You can extend traits with the extends
keyword and override an implementation with the override
keyword. 这句话可以理解为Traits可以继承或扩展。
scala> trait Greeter { def greet(name: String): Unit = println("Hello, " + name + "!") }defined trait Greeterscala> class DefaultGreeter extends Greeterdefined class DefaultGreeterscala> class CustomizableGreeter(prefix: String, postfix: String) extends Greeter { override def greet(name: String): Unit = { println(prefix + name + postfix) } }defined class CustomizableGreeterscala> val greeter = new DefaultGreeter()greeter: DefaultGreeter = DefaultGreeter@61bb1e4dscala> greeter.greet("Scala developer")Hello, Scala developer!scala> val customGreeter = new CustomizableGreeter("How are you, ", "?")customGreeter: CustomizableGreeter = CustomizableGreeter@5fb07347scala> customGreeter.greet("Scala developer")How are you, Scala developer?
上面的例子中,DefaultGreeter
只扩展了一个单个trait,不过它可以扩展多个traits。
Main Method
main方法是程序的入口点。JVM需要一个名为main的主方法并且需要一个字符串数组作为参数。
使用一个object,我们可以定义main方法如下:
scala> object Main { def main(args: Array[String]): Unit = println("Hello, Scala developer!") }defined object Main
上述main
方法的返回值Unit
类似其它语言中的void
,不过可以省略不写。但是main
方法的参数是不能省略的,不一定非得叫args
,如果省略了,就会出现错误提示"java.lang.NoSuchMethodException: Main.main([Ljava.lang.String;)
"。上面的object还可以这样写:
object World { def main(laven: Array[String]) { println("World") }}
行文至此,就先结束吧,想必你也拉完该提上裤子该去工作了。