工作如何繁忙,生活如何糟心,至少求知的这一刻是我的…
前言
基于Scala 2.12.14。学的《Scala for the Impatient》,记录每天的笔记,有些名词看的英文,翻得未必准确。
适用对象:Java程序员。尽可能在短时间内建立起Java到Scala的转换,做到能看能写。实际上在学习一门语言,应当逐步抛弃掉固有成见,才能发现其中乐趣。人生苦短,别用Java:(逃。
REPL
Read Evaluate Print Loop. 读取用户输入的表达式,并执行,打印结果的循环。严格来说Scala是没有REPL的。Scala的做法是将用户输入的表达式编译为字节码,并交由JVM执行。只是这过程足够快,造成我们似乎是解释执行的错觉。
REPL的优势在于可将一些开发中的想法直接即时地执行出结果,快速确认可行性。因此需要了解下如何使用交互式的命令行。
安装scala后,并配置了环境变量后,即可在终端中唤出交互式命令行:
scala
# Welcome to Scala 2.12.14 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_281).
# Type in expressions for evaluation. Or try :help.
scala> BigInt.probablePrime
def probablePrime(bitLength: Int,rnd: scala.util.Random): scala.math.BigInt
tips: 在Scala的交互式命令行中是可以使用tab补全的。
var与val
var与val分别是可变量和不可变量。
scala> val unchangeable = "This is a string."
unchangeable : String = This is a string.
scala> var changeable = 1
changeable: Int = 1
scala> unchangeable = "This is a string too."
<console>:12: error: reassignment to val
unchangeable = "This is a string too."
scala> changeable = 2
changeable: Int = 2
可以从上代码得出结论:
- val是不可变量,在给变量初始化后,就不可再修改其值。
- var是可变量,在给变量初始化后,可再修改其值。
那么什么时候应该使用val,什么时候用var?就如关键字的字面意思,可变量用var,不可变量用val。
再细看代码,从Java中,我们申明一个变量必然需要申明它的类型,不论是具体类型还是泛型。而在Scala中,在给变量初始化时即可从其值自动推导类型。
当然,我们也可显式地为变量确定类型:
val foo: String = "String"
val bar: Any = 1
val i, j = 2
var foobar, barfoo: String = null
常用类型
Scala有一些常用类型:Byte, Char, Short, Int, Long, Float, Double, Boolean。看起来似乎是Java中的包装类,事实上,Scala也只是类似Java,并不是复用的Java基本类型的包装类,它提供了比Java更多的便捷方法。
scala> 1.toString()
res1: String = 1
scala> 1.to(3)
res2: scala.collection.immutable.Range.Inclusive = Range 1 to 3
上边的代码在Java中简直是不可想象的事情,一个基本类型怎么可能可以调用方法呢?实际上,Scala没有Java中基本类型的概念,但它的编译器在将Scala代码编译成字节码时,会尽可能使用Java的基本类型。
这些数据类型也没有直接提供这些方法,而是编译器将其隐式转换为xxxOps对象(如StringOps)或Richxxx对象(数值类型),比如一个上述代码,1被转换为RichInt对象,由RichInt类提供便捷的操作方法。
试试12.3.toInt?
数学方法与操作符重载
+, -, *, /, %,一切有同Java。需要注意Scala中没有++和–操作,但提供了+=和-=,因此可以+=1和-=来替代。
那么位操作呢?&, |, ^, ~, <<, >>, >>>,同Java。
一些奇怪的调用方式:
a + b
// 等价于
a.+b
1.to(3)
// 等价于
1 to 3
实际上这是操作符重载特性,上边的+和to实际上都是方法,如果接触过C++或Python,就会对操作符重载有了解,有兴趣自己搜索。通过操作符重载,Scala定义了一些数据类型的数学操作,如BigInt和BigDecimal。
scala> val n: BigDecimal = 123.4
n: BigDecimal = 123.4
// 在Java中,这可是对象,无法使用*和/这样的运算符,得老老实实调用方法
scala> n * 2 / 2
res4: scala.math.BigDecimal = 123.4
更抽象地去看, a method b 等价于 a.method(b)。在Java中,更常用obj.method(param)这种方式调用方法。至于哪种方式更适合工程实践,那就仁者见仁智者见智了。
调用方法
常见调用方法
在上一节实际上已经有两种方法等价调用方式了:
- a method b
- a.method(b)
在方法无入参的情况,可以省略(),如
scala> "String".sorted
res5: String = Sginrt
类似Java中的静态方法调用:
// 这里的_等价于Java中的*
// 所有scala开头的package,都可以省略掉scala.,因此其等价于import math._
scala> import scala.math._
import scala.math._
// 其实更像Java中的import static ...
scala> max(1,2)
res6: Int = 2
apply方法调用
特殊的方法: apply。当类实现了名为apply的方法时,会发生一些令Javaer惊讶的事情:
scala> val str = "str"
str: String = str
// 这是方法调用吗?str是函数吗?
// 在上边刚申明的str变量是String类型,不是函数/方法
scala> str(1)
res7: Char = t
当类实现了名为apply的方法时,用括号传递给类实例或单例对象名一个或多个参数时,Scala会在相应的类或对象中查找方法名为apply且参数列表与传入的参数一致的方法,并用传入的参数来调用该apply方法。
当然也可显式地调用apply方法:
scala> str.apply(1)
res8: Char = t
因此Scala中会出现一些明明没有调用new对象,却又能生成对象的调用:
// Java中,应当是new BigInteger(123)
// 但在Scala中,它调用的是apply方法,该apply方法创建了一个BigInt对象。
val bi = BigInt(123)
无参方法调用注意事项
当一个方法本就无参,如果又想对输出结果调用apply方法:
// 期望输出s
scala> "str".sorted(1)
<console>:15: error: type mismatch;
found : Int(1)
required: scala.math.Ordering[?]
"str".sorted(1)
可以通过以下方法规避:
scala> "str".sorted.apply(1)
res10: Char = s