Scala基础语法
Scala与Java的最大区别是:Scala语句末尾的分号是可选的
我们可以认为Scala程序是对象的集合,通过调用彼此的方法来实现消息传递,理解类,对象,方法,实例变量的概念
- 对象:对象有属性和行为,对象是一个类的实例
- 类:类是对象的抽象,而对象是类的具体实例
- 方法:方法描述的基本的行为,一个类可以包含多个方法
- 字段:每个对象都有它唯一的实例变量集合,即字段,对象的属性通过给字段赋值来创建
交互式编程
脚本形式
创建一个HelloWorld.scala的文件来执行代码
object HelloWorld{
def main(args:Array[String]){
println("Hello,World!")
}
}
执行scala的编译命令
可以看到文件夹中生成了HelloWorld.class文件
基本语法
Scala基本语法需要注意以下几点
和java差不多的
- 区分大小写:Scala是大小写敏感的,这意味着标识Hello 和 hello在Scala中会有不同的含义
-
类名:对于所有的类名的第一个字母要大写。
如果需要使用几个单词来构成一个类的名称,每个单词的第一个字母要大写。 -
方法名称:所有的方法名称的第一个字母用小写。
如果若干单词被用于构成方法的名称,则每个单词的第一个字母应大写 -
程序文件名:程序文件的名称应该与对象名称完全匹配(新版本不需要了,但建议保留这种习惯)。
保存文件时,应该保存它使用的对象名称(记住Scala是区分大小写),并追加”.scala”为文件扩展名。 (如果文件名和对象名称不匹配,程序将无法编译)。
标识符
Scala可以使用两种形式的标志符,字符数字和符号
字符数字使用字母或是下划线开头,后面可以接字母或是数字,符号‘
’
在
S
c
a
l
a
中
也
看
作
为
字
母
,
然
而
以
’
’在Scala中也看作为字母,然而以’
’
在
S
c
a
l
a
中
也
看
作
为
字
母
,
然
而
以
’
‘开头的标识符为保留的Scala编译器产生的标志符使用,应用程序应该避免使用“$”开始的标识符,以免造成冲突
Scala 的命名规则采用和 Java 类似的 camel 命名规则,首字符小写,比如 toString。类名的首字符还是使用大写。此外也应该避免使用以下划线结尾的标志符以避免冲突。符号标志符包含一个或多个符号,如+,:,? 等,比如:
- ++ ::: < ?> :->
Scala 内部实现时会使用转义的标志符,比如:-> 使用
c
o
l
o
n
colon
c
o
l
o
n
minus$greater 来表示这个符号。因此如果你需要在 Java 代码中访问:->方法,你需要使用 Scala 的内部名称
c
o
l
o
n
colon
c
o
l
o
n
minus$greater。
混合标志符由字符数字标志符后面跟着一个或多个符号组成,比如 unary_+ 为 Scala 对+方法的内部实现时的名称。字面量标志符为使用”定义的字符串,比如
x
yield
。
你可以在”之间使用任何有效的 Scala 标志符,Scala 将它们解释为一个 Scala 标志符,一个典型的使用为 Thread 的 yield 方法, 在 Scala 中你不能使用 Thread.yield()是因为 yield 为 Scala 中的关键字, 你必须使用 Thread.
yield
()来使用这个方法。
Scala关键字
不是使用以下符号作为变量
Scala注释
Scala类似Java支持单行和多行注释,多行注释可以嵌套,但必须嵌套,一个注释开始符号对应一个结束符号,注释在Scala编译中会被忽略
实例
跟java一模一样
object HelloWorld {
/* 这是一个 Scala 程序
* 这是一行注释
* 这里演示了多行注释
*/
def main(args: Array[String]) {
// 输出 Hello World
// 这是一个单行注释
println("Hello, world!")
}
}
空行和空格
一行中只有空格或带有注释,scala会认为是空行忽略它
换行符
Scala是面向行的语言,语句可以用分号(;)结束或换行符。Scala 程序里,语句末尾的分号通常是可选的。如果你愿意可以输入一个,但若一行里仅 有一个语句也可不写。另一方面,如果一行里写多个语句那么分号是需要的。
val s = "菜鸟教程",println(s)
Scala数据类型
有着和Java相同的数据类型
上表中列出的数据类型都是对象,scala没有java中的原生类型,在scala是可以对数字等基础类型调用方法的
Scala字面基础量
Scala非常简单且直观,接下来我们会详细介绍Scala字面量
-
整型字面量
整型字面量用于 Int 类型,如果表示 Long,可以在数字后面添加 L 或者小写 l 作为后缀。
0
035
21
0xFFFFFFFF
0777L -
浮点型字面量
如果浮点数后面有f或者F后缀时,表示这是一个Float类型,否则就是一个Double类型的
0.0
1e30f
3.14159f
1.0e100
.1 -
布尔型字面量
布尔型字面量有true和false -
符号字面量
符号字面量被写成: ‘<标识符> ,这里 <标识符> 可以是任何字母或数字的标识(注意:不能以数字开头)。这种字面量被映射成预定义类scala.Symbol的实例。
如: 符号字面量 ‘x 是表达式 scala.Symbol(“x”) 的简写,符号字面量定义如下 -
字符字面量
在 Scala 字符变量使用单引号 ’ 来定义,如下
‘a’
‘\u0041’
‘\n’
‘\t’ -
字符串字面量
在Scala字符串字面量使用双引号“来定义
“Hello,\nWorld!”
“菜鸟教程官网:www.runoob.com” -
多行字符串的表示方法
多行字符串用三个双引号来表示分割符,格式为“”“ ”“”“
实例如下
val foo = “”“菜鸟教程
www.runoob.com
www.w3cschool.cc
www.runnoob.com
以上三个地址都能访问””” -
Null值
空值是scala Null 类型
Scala.Null和scala.nothing是用统一的方式处理scala面向对象类型系统的某些”边界情况“的特殊类型
Null类是null引用对象的类型,他是每个引用类(继承自AnyRet的类)的子类,Null不兼容值类型 -
Scala转义字符
0到255间的Unicode字符可以用一个八进制转义序列来表示,即反斜线”\“后跟最多三个八进制,在字符或字符串中,反斜线和后面的字符序列不能构成一个合法的转义序列将会导致编译错误
object Test {
def main(args: Array[String]) {
println("Hello\tWorld\n\n" );
}
}
Scala变量
在Scala中使用关键词var声明变量,使用关键词val声明常量
声明变量,常量实例
var myVar : String = "Foo"
var myVar : String = "Too"
val myVal : String = "Foo"
变量类型声明
变量的类型在变量名之后等号之前声明,定义变量的类型的语法格式如下
var VariableName : DataType [= Initial Value]
或
val VariableName : DataType [= Initial Value]
变量类型引用
在Scala中声明变量和常量不一定要指明数据类型,在没有指明数据类型的情况下,其数据类型是通过变量或常量的初始值推断出来的。所以,如果在没有指明数据类型的情况下声明变量或常量必须要给出其初始值,否则会报错
var myVar = 10;
val myVal = "Hello, Scala!";
scala多个变量声明
Scala支持多个变量的声明
val xmax, ymax = 100 // xmax, ymax都声明为100
如果方法返回值是元组,我们可以使用val来声明一个元组
scala> val pa = (40,"Foo")
pa: (Int, String) = (40,Foo)
Scala访问修饰符
Scala 访问修饰符基本和Java的一样,分别有:private,protected,public。
如果没有指定访问修饰符,默认情况下,Scala 对象的访问级别都是 public。
Scala 中的 private 限定符,比 Java 更严格,在嵌套类情况下,外层类甚至不能访问被嵌套类的私有成员
Scala方法与函数
Scala有方法与函数,二者在语义的区别很小,Scala方式是类的一部分,而函数是一个对象可以赋值给一个变量,换句话说在类中定义的函数即是方法
Scala中方法跟Java的类似,方法是组成类的一部分
Scala中的函数则是一个完整的对象,Scala中的函数其实就是继承了Trait的类的对象
Scala中使用val语句可以定义函数,def语句定义方法
class Test{
def m(x: Int) = x + 3
val f = (x: Int) => x + 3
}
方法声明
Scala方法声明格式如下
def functionName ([参数列表]) : [return type]
如果不写等于号和方法主体,那么方法会被隐式声明为抽象,包含它的类型也是一个抽象类型
方法定义
方法定义由一个def关键字开始,紧接着是可选的参数列表,一个冒号:和方法的返回类型,一个等于号=,最后是方法的主体,Scala方法定义格式如下
def functionName ([参数列表]) : [return type] = {
function body
return [expr]
}
以上代码中return type可以是任意合法的scala数据类型,参数列表中的参数可以用逗号分隔
以下方法的功能是将两个传入的参数相加并求和
object add{
def addInt( a:Int, b:Int ) : Int = {
var sum:Int = 0
sum = a + b
return sum
}
}
如果方法没有返回值,可以返回为Unit,这个类似于Java的void,实例如下:
object Hello{
def printMe( ) : Unit = {
println("Hello, Scala!")
}
}
方法调用
Scala提供了多种不同的方法调用方式
以下是调用方法的标准格式
object Test {
def main(args: Array[String]) {
println( "Returned Value : " + addInt(5,7) );
}
def addInt( a:Int, b:Int ) : Int = {
var sum:Int = 0
sum = a + b
return sum
}
}
函数是scala语言的核心
举两个例子
递归函数
递归函数在函数式编程的语言中起着重要作用
object Test {
def main(args: Array[String]) {
for (i <- 1 to 10)
println(i + " 的阶乘为: = " + factorial(i) )
}
def factorial(n: BigInt): BigInt = {
if (n <= 1)
1
else
n * factorial(n - 1)
}
}
高阶函数
高阶函数就是操作其他函数的函数
object Test {
def main(args: Array[String]) {
println( apply( layout, 10) )
}
// 函数 f 和 值 v 作为参数,而函数 f 又调用了参数 v
def apply(f: Int => String, v: Int) = f(v)
def layout[A](x: A) = "[" + x.toString() + "]"
}
Scala闭包
闭包是一个函数,返回值依赖于声明在函数外部的一个或多个变量
闭包通常来讲可以简单的认为是可以访问一个函数里面局部变量的另外一个函数
实例
object Test {
def main(args: Array[String]) {
println( "muliplier(1) value = " + multiplier(1) )
println( "muliplier(2) value = " + multiplier(2) )
}
var factor = 3
val multiplier = (i:Int) => i * factor
}
输出结果
muliplier(1) value = 3
muliplier(2) value = 6
Scala数组
Scala语言中提供的数组是用来存储固定大小的同类型元素
声明数组
scala数组声明的语法格式
var z:Array[String] = new Array[String](3)
或
var z = new Array[String](3)
以上语法中,z声明一个字符串类型的数组,数组长度为3,可以存储3个元素,我们可以为每个元素设置值,并通过索引来访问每个元素
z(0) = "Runoob"; z(1) = "Baidu"; z(4/2) = "Google"
最后一个元素的索引使用了表达式4/2作为索引,类似于z(2)
也可以使用以下定义一个数组
var z = Array("Runoob", "Baidu", "Google")
处理数组
数组的元素类型和数组的大小都是确定的,所以当处理数组元素时,我们通常使用基本的for循环
object Test{
def main(args:Array[String]){
var myList = Array(1.9,2.9,3.4,3.5)
//输出所有数组元素
for(x<-myList){
println(x)
}
//计算数组所有元素的总和
var total =0.0
for(i<-0 to (myList.length-1)){
total += myList(i);
}
println("总和为"+total);
//查找数组中的最大元素
var max = myList(0)
for(i<- 1 to (myList.length-1)){
if(myList(i)>max) max = myList(i);
}
println("最大值为"+max);
}
}
输出结果为:
1.9
2.9
3.4
3.5
总和为 11.7
最大值为 3.5
多维数组
多维数组一个数组中的值可以是另一个数组,另一个数组的值也可以是一个数组,矩阵和表格是我们常见的二维数组
val myMatrix = Array.ofDim[Int](3, 3)
实例
import Array._
object Test {
def main(args: Array[String]) {
val myMatrix = Array.ofDim[Int](3, 3)
// 创建矩阵
for (i <- 0 to 2) {
for ( j <- 0 to 2) {
myMatrix(i)(j) = j;
}
}
// 打印二维阵列
for (i <- 0 to 2) {
for ( j <- 0 to 2) {
print(" " + myMatrix(i)(j));
}
println();
}
}
}
Scala集合
Scala提供了一套很好的集合实现,提供了一些集合类型的抽象。
Scala 集合分为可变的和不可变的集合。
可变集合可以在适当的地方被更新或扩展。这意味着你可以修改,添加,移除一个集合的元素。
而不可变集合类,相比之下,永远不会改变。不过,你仍然可以模拟添加,移除或更新操作。但是这些操作将在每一种情况下都返回一个新的集合,同时使原来的集合不发生改变。
各个集合的实例
// 定义整型 List
val x = List(1,2,3,4)
// 定义 Set
val x = Set(1,3,5,7)
// 定义 Map
val x = Map("one" -> 1, "two" -> 2, "three" -> 3)
// 创建两个不同类型元素的元组
val x = (10, "Runoob")
// 定义 Option
val x:Option[Int] = Some(5)
其中元组和Option的集合进行具体的了解
Scala元组
与列表一样,元组也是不可变的,但与列表不同是元组可以包含不同类型的元素
元组的值是通过将单个的值包含在圆括号中构成的
val t = (1,3.14,"Fred")
也可以使用以下方式来定义
val t = new Tuple3(1, 3.14, "Fred")
元组的实际类型取决与它的元素类型,比如 (99, “runoob”) 是 Tuple2[Int, String]。 (‘u’, ‘r’, “the”, 1, 4, “me”) 为 Tuple6[Char, Char, String, Int, Int, String]。
元组的最大长度为22,对于更大长度可以使用集合或者扩展元组
访问元组的元素可以通过数字索引,如下一个元组
val t = (4,3,2,1)
我们可以使用 t._1 访问第一个元素, t._2 访问第二个元素
object Test {
def main(args: Array[String]) {
val t = (4,3,2,1)
val sum = t._1 + t._2 + t._3 + t._4
println( "元素之和为: " + sum )
}
}
迭代元组
可以使用Tuple.productIterator()方法来迭代输出元组的所有元素
object Test {
def main(args: Array[String]) {
val t = (4,3,2,1)
t.productIterator.foreach{ i =>println("Value = " + i )}
}
}
元组转为字符串
可以使用Tuple.toString()方法将元组的所有元素组合成一个字符串
object Test {
def main(args: Array[String]) {
val t = new Tuple3(1, "hello", Console)
println("连接后的字符串为: " + t.toString() )
}
}
Scala Option(选项)
Scala Option类型用来表示一个值是可选的(有值或无值)
Option(T)是一个类型为T的可选值的容器,如果值存在,Option[T]就是一个Some[T],如果不存在,Option[T]就是对象None
val myMap: Map[String, String] = Map("key1" -> "value")
val value1: Option[String] = myMap.get("key1")
val value2: Option[String] = myMap.get("key2")
println(value1) // Some("value1")
println(value2) // None
在上面的代码中,myMap一个是一个key的类型是String,Value的类型是String的hash map,但不一样的是他的get()返回的是一个叫Option[String]的类别
Scala 使用 Option[String] 来告诉你:「我会想办法回传一个 String,但也可能没有 String 给你」。
myMap 里并没有 key2 这笔数据,get() 方法返回 None。
Option 有两个子类别,一个是 Some,一个是 None,当他回传 Some 的时候,代表这个函式成功地给了你一个 String,而你可以透过 get() 这个函式拿到那个 String,如果他返回的是 None,则代表没有字符串可以给你。
object Test {
def main(args: Array[String]) {
val sites = Map("runoob" -> "www.runoob.com", "google" -> "www.google.com")
println("sites.get( \"runoob\" ) : " + sites.get( "runoob" )) // Some(www.runoob.com)
println("sites.get( \"baidu\" ) : " + sites.get( "baidu" )) // None
}
}
也可以通过模式匹配来输出匹配值
object Test {
def main(args: Array[String]) {
val sites = Map("runoob" -> "www.runoob.com", "google" -> "www.google.com")
println("show(sites.get( \"runoob\")) : " +
show(sites.get( "runoob")) )
println("show(sites.get( \"baidu\")) : " +
show(sites.get( "baidu")) )
}
def show(x: Option[String]) = x match {
case Some(s) => s
case None => "?"
}
}
Scala Iterator(迭代器)
Scala Iterator不是一个集合,是一种用来访问集合的方法
迭代器it的两个基本操作是next和hashNext
调用it.next()会返回迭代器的下一个元素,并且更新迭代器的状态
调用it.hasNext()用于检测集合中是否还有元素
让迭代器it逐个返回所有元素最简单的方法是使用while循环
object Test{
def main(args:Array[String]){
val it = Iterator("Baidu", "Google", "Runoob", "Taobao")
while(it.hasNext){
println(it.next())
}
}
}
Scala 类和对象
类是对象的抽象,而对象是类的具体实力,类是抽象的,不占内存,而对象是具体的,占用存储空间,类是用于创建对象的蓝图,它是一个定义包括在特定类型的对象中的方法和变量的软件模板
可以使用new关键字来创建类的对象
class Point(xc: Int, yc: Int) {
var x: Int = xc
var y: Int = yc
def move(dx: Int, dy: Int) {
x = x + dx
y = y + dy
println ("x 的坐标点: " + x);
println ("y 的坐标点: " + y);
}
}
Scala的类定义可以有参数,称为类参数,如上面的xc,yc类参数在整个类中都可以访问
接着我们可以使用new来实例化类,并访问类中的方法和变量
import java.io._
class Point(xc: Int, yc: Int) {
var x: Int = xc
var y: Int = yc
def move(dx: Int, dy: Int) {
x = x + dx
y = y + dy
println ("x 的坐标点: " + x);
println ("y 的坐标点: " + y);
}
}
object Test {
def main(args: Array[String]) {
val pt = new Point(10, 20);
// 移到一个新的位置
pt.move(10, 10);
}
}
Scala继承
Scala继承一个基类跟java很相似,需要注意一下几点
- 重写一个非抽象方法必须使用override修饰符
- 只有主构造函数才可以往基类的构造函数里写参数
- 在子类中重写超类的抽象方法时,你不需要使用override关键字
class Point(xc: Int, yc: Int) {
var x: Int = xc
var y: Int = yc
def move(dx: Int, dy: Int) {
x = x + dx
y = y + dy
println ("x 的坐标点: " + x);
println ("y 的坐标点: " + y);
}
}
class Location(override val xc: Int, override val yc: Int,
val zc :Int) extends Point(xc, yc){
var z: Int = zc
def move(dx: Int, dy: Int, dz: Int) {
x = x + dx
y = y + dy
z = z + dz
println ("x 的坐标点 : " + x);
println ("y 的坐标点 : " + y);
println ("z 的坐标点 : " + z);
}
}
Scala使用extends关键字来继承一个类,实例中Location类继承了Point类,Point称为父类(基类),Location成为子类
override val xc 为重写了父类的字段
继承会继承父类的所有属性和方法,Scala只允许继承一个父类
import java.io._
class Point(val xc: Int, val yc: Int) {
var x: Int = xc
var y: Int = yc
def move(dx: Int, dy: Int) {
x = x + dx
y = y + dy
println ("x 的坐标点 : " + x);
println ("y 的坐标点 : " + y);
}
}
class Location(override val xc: Int, override val yc: Int,
val zc :Int) extends Point(xc, yc){
var z: Int = zc
def move(dx: Int, dy: Int, dz: Int) {
x = x + dx
y = y + dy
z = z + dz
println ("x 的坐标点 : " + x);
println ("y 的坐标点 : " + y);
println ("z 的坐标点 : " + z);
}
}
object Test {
def main(args: Array[String]) {
val loc = new Location(10, 20, 15);
// 移到一个新的位置
loc.move(10, 10, 5);
}
}
Scala单例对象
scala中没有static,单例模式的实现方式是使用关键字object
单例模式除了定义的类还要定义一个同名的object对象,它和类的区别是,object对象不能带参数
但单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象:companion object。你必须在同一个源文件里定义类和它的伴生对象。类被称为是这个单例对象的伴生类:companion class。类和它的伴生对象可以互相访问其私有成员。
import java.io._
class Point(val xc: Int, val yc: Int) {
var x: Int = xc
var y: Int = yc
def move(dx: Int, dy: Int) {
x = x + dx
y = y + dy
}
}
object Test {
def main(args: Array[String]) {
val point = new Point(10, 20)
printPoint
def printPoint{
println ("x 的坐标点 : " + point.x);
println ("y 的坐标点 : " + point.y);
}
}
}
伴生对象实例
/* 文件名:Marker.scala
* author:菜鸟教程
* url:www.runoob.com
*/
// 私有构造方法
class Marker private(val color:String) {
println("创建" + this)
override def toString(): String = "颜色标记:"+ color
}
// 伴生对象,与类名字相同,可以访问类的私有属性和方法
object Marker{
private val markers: Map[String, Marker] = Map(
"red" -> new Marker("red"),
"blue" -> new Marker("blue"),
"green" -> new Marker("green")
)
def apply(color:String) = {
if(markers.contains(color)) markers(color) else null
}
def getMarker(color:String) = {
if(markers.contains(color)) markers(color) else null
}
def main(args: Array[String]) {
println(Marker("red"))
// 单例函数调用,省略了.(点)符号
println(Marker getMarker "blue")
}
}
Scala Trait(特征)
ScalaTrait(特征)相当于Java的接口,比接口功能更加强大
与接口不同的是,它还可以定义属性和方法的实现
一般情况下Scala的类只能继承单一父类,但是如果是Trait的话可以继承多个,从结果来看就是实现了多重继承
Trait定义的方式与类类似,但它使用的关键字是trait
trait Equal{
def isEqual(x: Any): Boolean
def isNotEqual(x: Any): Boolean = !isEqual(x)
}
以上trait由两个方法组成:isEqual和isNotEqual,isEqual方法没有定义方法的实现,isNotEqual定义了方法的实现,子类继承特征可以实现未被实现的方法,所以其实scala更像java的抽象类
实例
trait Equal {
def isEqual(x: Any): Boolean
def isNotEqual(x: Any): Boolean = !isEqual(x)
}
class Point(xc: Int, yc: Int) extends Equal {
var x: Int = xc
var y: Int = yc
def isEqual(obj: Any) =
obj.isInstanceOf[Point] &&
obj.asInstanceOf[Point].x == x
}
object Test {
def main(args: Array[String]) {
val p1 = new Point(2, 3)
val p2 = new Point(2, 4)
val p3 = new Point(3, 3)
println(p1.isNotEqual(p2))
println(p1.isNotEqual(p3))
println(p1.isNotEqual(2))
}
}