REPL

Scala解释器读到一个表达式,对它进行求值,将它打印出来,接着再继续下一个表达式。这个过程被称作“读取-求值-打印”循环(read-eval-print loop),即REPL

var和val的区别

  • var是一个可变变量,是一个可以通过重新分配来更改为另一个值的变量。这种声明变量的方式和java中声明变量的方式一样
  • val是一个只读变量,这种声明变量的方式相当于java中的final变量。一个val创建的时候必须初始化,因为以后不能被改变。
1
2
3
4
5
6
7
8
9
//var 即variable,用于声明变量
var str : String = "Hello Scala!"

//val 即value,用于声明常量,类似Java中的final,使用val定义的常量不允许重新赋值
val num : Int = 100;
scala> num = 200
<console>:12: error: reassignment to val
num = 200
^

Scala编译器可以通过值的类型推断出变量的类型,因此变量类型可以省略

常用类型

和Java一样,Scala也有七种数值类型:Byte、Char、Short、Int、Long、Float和Double,以及一个Boolean类型。与Java不同的是,这些类型是类。Scala并不刻意区分基本类型和引用类型。可以对数字执行方法

1
1.toString()  // 将交出字符串“1”

在Scala中,不需要包装类型,在基本类型和包装类型之间的转换是Scala编译器的工作。

Scala用底层的java.lang.String类来表示字符串,通过StringOps类给字符串追加了上百种操作,如:

1
2
scala> "hello".intersect("World")
res0: String = lo

在这个例子中,java.lang.String对象”Hello”被隐式地转换成了一个StringOps对象,接着StringOps类的intersect方法被应用。

算术和操作符重载

+ - * / 与在Java中完成的工作一样,但这些操作符实际上是方法,比如 a + b 是 a.+(b) 的简写,这里的+是方法名。通常来说, a 方法 b可以写成 a.方法(b)

1
2
1 to 10 可以写成 1.to(10)
输出都为 Range 1 to 10

值得注意的是,Scala中没有 ++ 和 – 操作符,需要递增或者递减时,使用 += 1 或 -= 1

方法调用

1
2
3
4
5
6
import scala.math._
// 这里的 _ 字符表示通配符,类似于Java中的 * ,然后就可以直接使用调用包中的方法
sqrt(2) // 将交出 1.4142135623730951

// 如果不引入scala.math 包,则添加包名来使用
scala.math.sqrt(2)

注意一点,使用以scala. 开头的包时,可以省去scala前缀,例如 import math._ 等同于 import scala.math_

apply方法

在Java中,如果需要得到字符串中的第i个字符,那么需要用到 s.charAt(i),在Scala中,则使用 s(i) 进行表示,它背后的实现原理是一个名为apply 的方法。在StringOps类的文档中,有这么一个方法

1
def apply(n : Int): Char

也就是说,s(i) 是 s.apply(4)

如果是在多个方法中使用apply方法,则可以把apply方法前的表达式圈起来

1
2
3
4
5
6
"HelloWorld".sorted(3)  // 这将会引发一个错误,因为sorted 方法可以用一个隐式的排序参数来调用,但3并不是一个有效的排序

// 因此可以如下使用
("HelloWprld".sorted)(3)
或者
"HelloWorld".sorted.apply(3)

获取首尾字符

1
2
3
4
5
6
7
8
9
10
11
scala> var s = "HelloWorld"
s: String = HelloWorld

scala> s(0)
res0: Char = H

scala> var x = s.reverseIterator
x: Iterator[Char] = <iterator>

scala> x.next()
res1: Char = d

take,drop,takeRight和dropRight 字符串函数区别

1
2
3
4
5
6
7
8
9
10
take、drop、takeRight、dropRight 位于StringOps中

def take (n:Int):String // 获取前n个元素
def takeRight(n: Int): String //获取最后n个元素
def drop(n: Int): String //获取所有元素,除了前n个
def dropRight(n: Int): String //获取所有元素,除了最后n个

substring调用的是java的方法
def substring(arg0: Int): String // 截取所有字符,除了前n个,类似drop(索引从1开始)
def substring(arg0: Int, arg1: Int): String //从索引[arg0,arg1)截取字符串(索引从0开始)

伴生对象

scala中的类不能定义静态成员,而代之以定义单例对象来替代,单例对象通过object关键字来声明,单例对象中的所有方法,可以直接通过object单例对象的名字直接来调用。

一个单例对象可以绑定在一个类,当单例对象和某个类写在同一个源文件且共享一个名字,它们就产生了绑定关系。此时单例对象称之为该类的伴生对象,类称之为该对象的伴生类。

1
2
3
4
5
6
7
8
9
10
11
object food {
def getFood(){
println("this is not the same")
}
}

class food{
def getFood(): Unit ={
println("this is your food.")
}
}

类和它的伴生对象可以互相访问其私有成员,单例对象不能new,所以也没有构造参数,可以把单例对象当做java中可能会用到的静态方法工具类。

作为程序入口的方法必须是静态的,所以main方法必须处在一个单例对象中,而不能写在一个类中。

参考博客:scala 伴生对象的作用
参考博客:学习Scala:伴生对象的实现原理