数据类型
String
String 本不算什么基础类型,Scala String 类型有些特点简单介绍几点
字符串插值(String Interpolation)
字符串插值一句话解释:能在字符串定义中直接嵌入变量的引用,举个例子:
val name = "wuzheng"
println(s"hello $name ") // hello wuzheng
第二句中的$name
是引用了前面定义的变量name
在字符串定义前加s
,¥{}
当然也可以嵌入表达式。
println(s"1 + 1 = ${1 + 1}") // 1 + 1 = 2
本质上s
是一个function ,自然可以定义其他的函数,比如scala 自己就提供了f
用于格式化输出。
val name = "wuzheng"
val height = 1.9d
println(f"$name%s is $height%2.2f meters tall") // wuzheng is 1.90 meters tall
上面这个例子改自官方文档(当然我没那么高)。这种格式化Python 里面是有相似的东西的。python 里也有相似的东西比如在字符串开头添加r
来屏蔽字符串转意比如:
print(r"Hello \n wuzheng")
上面这句就不会输出换行符,而是直接输出\n
Scala 也有,不过不是r
而是raw
println(raw"hello \n wuzheng")
当然可以自定义插入的符号,因为本质上这些都是函数,字符串插值应该是Scala2.10之后才有的。字符串本身还有正则,等一些操作,感觉和其他语言差异不大,不再细述。
数值
Scala 有如下几种内建数值类型
Char 16位无符号Unicode
Byte 8位有符号整数
Short 16位有符号整数
Int 32位有符号整数
Long 64位有符号整数
Float
Double
自增自减
Scala 和 和Python 一样没有++
和--
运算。
浮点数的比较
两个本来相等的浮点数可能时间上不相等比如
scala> val b = 0.1 + 0.2
b: Double = 0.30000000000000004
而且
scala> b == 0.3
res0: Boolean = false
可以自定义近似相等来解决比如
def simEqual(x: Double, y: Double, precision: Double) = {
if ((x - y).abs < precision) true else false
}
产生整数序列
Python 有range
函数,Scala 提供了,to
和until
.until
得到的是包含下界,不包含上界的半包半闭区间,to
则包含下界和上界。
控制结构
Scala 的控制结构很多地方是我很难适应的,从C, C++, Java ,Python 一路走来控制结构几乎没有多大的变化,但是Scala 做出了很大的改变。
去掉的关键字
Scala 没有了break
,continue
,甚至return
关键字,return
没了还好,如故是函数就是做后一句写上要返回的变量。但是如果你习惯了在控制结构中使用return
估计就麻烦了。还有,很多时候,特别是C,C++,Java 的时候没有break
,continue
这两个东西那真是”宝宝办不到“啊。
至于这其中的原因,和好处估计得慢慢体会了。
还好Scala 提供了类似的东西来替代,虽然麻烦了但是非用不可的时候还是需要的。
object ControllExample {
def main(args: Array[String]) {
breakable {
for (i <- 1 to 10) {
println(i)
if (i > 5) {
break
}
}
}
}
}
Continue 也可类似处理。
for 循环的特殊之处
卫语句
意思是for 中嵌入if 语句,比如输出1到10偶数
for (i <- 1 to 10 if i % 2 == 0) {
println(i)
}
for/yield
for
和 yield
结合感觉和Python的迭代器类似,会产生一个可迭代 的对象。
val a = Array("wu", "zheng", "Hello")
val lengths = for(e<-a) yield {
e.length()
}
lengths.foreach(println(_))
switch 和模式匹配
说老实话swith 我一般用得较少,但是Scala 的switch 我觉得不能不说,结合Scala 的模式匹配,感觉就不得不提了。
val i = 1
val x = i match {
case 1 => "one"
case 2 => "two"
case _ => "many"
}
这感觉是不是和switch 和相似?,都推荐使用@switch
在不能编译成tableswitch
或lookswitch
的时候会有警告。
val i = 1
val x = (i: @switch) match {
case 1 => "one"
case 2 => "two"
case _ => "many"
}
一个case
可以匹配多个值
val i = 4
val x = (i: @switch) match {
case 1 | 2 => "one"
case 4 | 3 => "two"
case _ => "many"
}
println(x)
case 语句中可以使用,常量模式,变量模式,构造函数模式,序列模式,元组模式,或者类型模式。
def echoCase(x: Any): String = x match {
case 0 => "zero"
case true => "true"
case "hello " => "hello wuzheng"
case Nil => "空数组"
case List(0, _, _) => "三个元素的序列,其中第一个为0"
case List(1, _*) => "第一个元素为1序列"
case s: String => s"输入的值是 String : $s"
case i: Int => s"输入的是整数: $i"
case _ => "我不知道了"
}
try/catch 也是使用case 捕获异常
val s = "wu zheng"
try {
val i = s.toInt
}catch {
case e: Exception => e.printStackTrace()
}
函数
Scala 不是单纯的OOP 语言,它还是FP语言,这和Python 很类似 但又有很多强大的特性。
匿名函数
在Python 中有lambda
关键字定义匿名函数,Scala 的更直接
scala> (x: Int) => x+1
res8: Int => Int = <function1>
scala> res8(3)
res9: Int = 4
可以把匿名函数保存为变量,或直接进行传递
var a = List(1,2,3)
val addOne = (x: Int) => x + 1
a.map(addOne)
部分应用(Partial application)柯里化(Currying)
部分应用,和Python的函数默认参数类似,但是你可以随意改变默认参数的值,这样就能得到很多不同默认参数的函数了。
val sum = (a: Int, b: Int, c: Int) => a + b + c
val f = sum(1, 2, _: Int)
println(f(3))
把函数定义方式变一下,可以实现Currying ,柯里化的意思shi把原来接收两个参数的函数变成接收一个参数的函数
def multiply(m: Int)(n: Int): Int = m * n
val multiT = multiply(2) _
println(multiT(3))
闭包
这部分感觉还没理解太透,看到一个例子
var factor = 3
val multiplier = (i:Int) => i * factor
意思是函数multiplier 可以访问变量factor, 有段话是这样解释的
闭包是一个函数,返回值依赖于声明在函数外部的一个或多个变量。
闭包通常来讲可以简单的认为是可以访问一个函数里面局部变量的另外一个函数。
OOP
类的定义
class Calculator(brand: String) {
/**
* 构造函数部分
*/
val color: String = if (brand == "TI") {
"blue"
} else if (brand == "HP") {
"black"
} else {
"white"
}
// 实例方法
def add(m: Int, n: Int): Int = m + n
}
构造函数就是除了方法之外的代码,还可以定义辅助构造函数,有点像Java 的构造函数的重载。使用this
关键字
def this(brandSize: Int,){
this("a")// 调用之前的构造函数
}
辅助构造函数必须满足:
用this关键字为名创建
必须从调用之前定义的构造函数开始
每个构造函数要有不同的签名
一个构造函数通过this调用另一个构造函数
Traits(特质)
Traits(特质) 类似Java 的接口(interfaces),不同的是,允许用户对不放方法在Traits中实现。
trait Similarity {
def isSimilar(x: Any): Boolean
def isNotSimilar(x: Any): Boolean = !isSimilar(x)
}
抽象类
和Traits 类似,可以被子类继承,但是不同的是,可以有自己的主构造函数,Traits 是没有构造函数的。在和Java 交互的时候也需要定义抽象类。所以需要有主构造函数的时候就使用抽象类。但是通常是使用Traits因为后者更加灵活。
继承
一个类只能继承一个抽象类,但是一个可以继承多个特质。
如果一个类继承了特质使用
extends
如果继承了多个特质第一个使用extends
其余的使用with
如果一个类继承了一个类和一个特质,继承类使用extends
特质使用with
abstract class Animal {
}
trait WaggingTail{
}
class Dog extends Animal with WaggingTail {
}
Mixin(混入)
官方文档有详细的例子和说明。意思就是可以通过extends
或者with
获得一个或多个特质的特性
数据结构
基础数据结构
List
感觉和Python的list 挺像,不同的是取元素的时候是使用()
.List中也是可以放入不同类型的。
val numbers = List(1, 2, 3, 4)
numbers(1) // 2
Set
不重复集合
val s = set(1,2,3,4,2)
元组
val hostPort = ("localhost", 80)
hostPort._1 //第一个元素
hostPort._2 // 第二个元素
Map
Map(1 -> 2)
Map("foo" -> "bar")
函数组合子(Functional Combinators)
不太好说这个定义Stackoverflow上有个说明 可以去看下,搞不清楚就直接看例子好了。其实我理解就是一些能把函数应用在数据结构上的方法,比如map
,filter
flatmap
等等。
map
scala> val numbers = List(1, 2, 3, 4)
numbers: List[Int] = List(1, 2, 3, 4)
scala> numbers.map((i: Int) => i * 2)
res14: List[Int] = List(2, 4, 6, 8)
foreach
和map 类似有一个隐式循环,但是没有返回值
numbers.foreach((i: Int) => i * 2) //不会产生一个新的List
filter
去掉那些不满足条件的元素,得到一个新的结构
numbers.filter((i: Int) => i % 2 == 0)
zip
Python 也有的哟,很好用。
scala> List(1, 2, 3).zip(List("a", "b", "c"))
res0: List[(Int, String)] = List((1,a), (2,b), (3,c))
partition
可以分割列表
scala> val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> numbers.partition(_ % 2 == 0)
res0: (List[Int], List[Int]) = (List(2, 4, 6, 8, 10),List(1, 3, 5, 7, 9))
find
和filter 操作相反,返回满足条件的元素
numbers.find((i: Int) => i > 3)
还有好多看这里
隐式转换
这是我最后想说的东西。如果要给一个类添加一些自定义方法,在Java中需要继承,然后定义一个方法,在Scala有隐式转化来实现。
举个简单的例子,把字符串的每个字符值加1得到一个新的字符串
implicit class StringImprovements(s:String){
def increment = s.map(c => (c+1).toChar)
}
println("KNUD".increment) //输出:LOVE
理想很丰满,现实总是很骨干,不会Java 来用Scala总是感觉在耍流氓
参考:Scala DOCUMENTATION
Scala School