《大数据开发语言Scala入门》
Scala
1 基础
Scala中使用方法而不是强制类型转换来做数值类型之间的转换
没有++,——因为Int类是不可变的
a 方法 b 与 a.方法(b) 一样
数学函数位于 scala.math 中,可以像函数一样使用,实际上应该是伴生对象的方法
一般情况下,没有参数并不改变当前对象的方法不带圆括号
apply
使用伴生对象的apply方法是scla中构建对象的常用方法
2 控制结构和函数
条件表达式是有值的,可以分支的类型不一样,其结果类型为分支的公共超类型。
:paste 用以REPL中粘贴多行代码,避免短视
if (x>0) 1 else -1
if (x>0) 1 // if (x>0) 1 else ()
跨行:可以使用括号,未结束的操作符
块语句是指位于 {} 中的语句序列,包含一系列表达式,结果也是表达式,最后一个表达式的值就是语句块的值
赋值本身是没有值的,其值是 Unit 类型的 () 值
输入输出:print println printf, readLine readInt
while / do while 循环:
while (n>0){
...}
for 循环
for (i <- 1 to 6) ..
for (i <- 1 to 6; from = 4-i;j <- from until 9
if i!=j ;)
for (...) yield {
}`
对于递归函数,必须指定返回类型
默认参数,带名参数与Python接近
变长参数: def sum(args:Int*) …
使用: sum(1 ot 5:_*)
函数体包含在花括号中但没有等号的返回类型是Unit,称为过程,不返回值,调用它只是为了副作用
当val被声明为lazy时,初始化将被推迟,直到首次对它取值
懒值也有开销,访问时都有线程安全方式检查该值是否已经被初始化
val words = //当时初始化
lazy val wrods = //第一次使用时初始化
def words = //每次使用时均求值
异常表达式的类型是Nothing,在if/else中,则返回类型是非Nothing分支的类型
异常捕获使用模式匹配
tray catch finally
3 数组相关操作
若长度固定使用Array,可能有变化使用ArrayBuffer
-
val nums = new Array[Int](10)
-
val nums2 = Array(1,2,3);nums(0)=4
-
import scala.collection.mutable.ArrayBuffer
-
val b = ArrayBuffer[Int]() / new ArrayBuffer[Int]
-
b += 1
-
b += (1,3,4)
-
b ++= Array(4,5,6)
-
b.trimEnd(5)
-
b.insert(2, x)/insert(2, x,y,z)
-
b.remove(2)/remove(2,3)
-
b.toArray / a.toBuffer
提供初始值时不要使用new
用()来访问元素
用for (elem <- arr) 来遍历数组
(0 until (a.length,2)).reverse
用 for (elem <- arr if …) yield …来将数组转变为新数组
集合类型与原集合相同,Array to Array,Buffer to Buffer
可以使用 .filter().map() 替换
var first = true
val indexes = for (i <- 0 until array.length if first || array(i)>=0) yield {if (array(i)<0) first=false;i}
for (i <- 0 until indexes.length) {a(i)=a(indexs(i))}
a.trimEnd(a.length - indexs.length)
常见函数
sum /max /min /b.sorted(\_ < \_)(返回排序后的)
scala.util.Sorting.quickSort(a) (不能使用在ArrayBuffer)
a.mkString( str )/ .mkString(a,b,c)
多维数组:数组的数组来实现
val m = Array.ofDimInt
m(3)(4) = 7
val t = new ArrayArray\[Int\]
for (i <- 0 to 9){
t(i)=new ArrayInt}
Scala数组和Java数组可以互操作;用ArrayBuffer,使用scala.collection.JavaConversions中的转换函数
4 映射和元组
对偶是n=2的元组
创建、查询和遍历映射
-
val m = Map('a'->3, 'b'->2, 'c'->4) //->构建对偶
-
val m = Map(('a',3),('b',2), ('c',4))
-
val m = scala.collection.mutable.Map(...)
-
val m = new scala.collection.mutable.HashMap[String,Int]
-
m('a')
-
if (m.contains('a')) m('a') else 0
-
m.getOrElse('a', 0)
-
m.get('a') //返回一个Option对象,值或者None
-
m += ('c'->4, 'e'->7)
-
m -= 'a'
-
val m3 = m1 + ('c'->3...) //新旧结构会共享
-
for ((k,v) <- m) ....
-
for (k <- m.keySet) ...
-
for (v <- m.values) ...
-
for ((k,v) <- m) yield (v,k)
-
scala.collection.immutable.SortedMap //树Map
-
scala.collection.mutable.LinkedHashMap //按插入顺序处理
需要从可变和不可变的映射中作出选择
默认情况下获得哈希映射,可以指明要树形映射
可以很容易在scala与java映射之间切换
import scala.collection.JavaConversions.mapAsScalaMap
元组可以用来聚集值
元组是通过将单个的值包含在圆括号中构成的,可用于返回不止一个值的情况
val t = new Tuple3[Int,Int,String]
t._1, t._2, t._3
val (first, sencond, _) = t//匹配模式获得值
zip操作
zip将Array合并成元组的Array
keys.zip(values).toMap
5 类
所有类都具有公有可见性
带getter和setter的属性
getter和setter分别叫做 x 和 x_=,可以自己重定义
def age = privateAge
def age_=(age: Int) {privateAge = age}
统一访问原则:某个模块提供的所有服务都应该是通过统一的表示法访问到,不管其是存储还是通过计算实现的
- 字段是私有的,则getter和setter方法也是私有的
- 字段是 val ,则只生成getter
- 不需要getter和setter,则可以设置 private[this]
- 自己可更改,外界不可改,则实现私有变量,访问函数
方法可以访问该类的所有对象的私有变量,包括其他实例
使用 private[this] … 则只能访问本实例this的字段
将字段标注为 @BeanProperty 可生成Java的默认访问方法getXX, setXX
import scala.reflect.BeanProperty
class Person{
-
`@BeanProperty var name: String = _`
}
辅助构造器的名字为 this;每个辅助构造器都必须以一个先前已经定义的其他辅助构造器或主构造器的调用开始。没有显式定义主构造器则自动拥有一个无参数的主构造器
def this(name: String){
this()
this.name = name
}
主构造器:与类定义交织在一起,参数直接放在类名之后。参数被编译成字段,其值被初始化成构造时传入的参数。主构造器会执行类定义中的所有语句
class Person(val name:String,private val age:Int){
...
}
不带val或var的参数如果在类中至少被一个方法使用,则其升级为字段,相当于private[this] val。否则就仅是一个普通参数,供主构造器中代码访问。
可以在任何语法结构中内嵌任何语法结构。类中定义类。
每个实例都有它自己的内嵌类(泛型导致,实例有可能是不一样类型,其内嵌类也有可能不一样),进行操作时需要注意。需要公用,则可将其移入伴生对象中。或者使用类型投影。 外部类.this 访问外部类的 this 引用。
class NetWork(val name:Int){
outer => //指向 NetWork.this`
class Member(){
... // use outer.name
}
}
6 对象
用对象作为单例或存放工具方法/常量或高效共享不可变实例
使用object创建单例,对象的构造器在其首次调用时指向。如果从未被使用,则其构造器不会被执行。其本质上可拥有类的所有特性,除了不能提供构造器参数。
类可以拥有一个同名的伴生对象
类与其伴生对象可以相互访问私有特性,必须存在同一个源文件中。
对象可以扩展类或者特质
其结果是一个扩展了指定类以及特质的类的对象,同时拥有在对象定义中给出的所有特性。
对象的apply方法通常用来构造伴生类的新实例
通常会定义和使用对象的apply方法,遇到如下形式的表达式,apply方法会被调用。返回的是伴生类的对象。可以省略使用 new
object(para1, ... paran)
如果不想显式定义main方法,可以扩展App特质的对象
执行程序必须从某个对象的main方法开始
object Hello{
def main(args:Array[String]){
...
}
}
object Hello extends App{
if (args.length ...)...
}
可以通过扩展Enumeration对象来实现枚举
object TrafficLightColor extends Enumeration{
val Red, Yellow, ... = Value
val Red = Value(0, "stop")
val Red = Value(10) //类型为TrafficLightColor.Value
val Red = Value("Stop") //每次调用Value都返回新实例
type TrafficLightColor = Value //类型别名
}
import TrafficLightColor._
for (c <- TrafficLightColor.values) ...
TrafficLightColor(0)
TrafficLightColor.withName("Stop")
7 包和引入
包可以像内部类那样嵌套
源文件的目标与包之间没有强制的关联关系。可以在一个文档中定义多个包中的内容。
包路径不是绝对路径
包支持嵌套,可以访问上层作用域中的名称。如果需要访问非直接嵌套域中的包,则可以使用绝对包名 root. …
包声明链x.y.z并不自动将中间包x和x.y变成可见
package com.horstman.xx.xx{
// com._, horstman_, xx_ 是不可见的
}
位于文件顶部不带花括号的包声明在整个文件范围内有效
package x.y.z
package m
//相当于
package x.y.z{
package m{
...
}
}
包对象可以持有函数和变量
包可以包含类、对象和特质,但不能包含函数或变量的定义。Java虚拟机的限制。每个包可以有一个包对象,需要在父包中定义,且名字与子包一样
package x.y.x
package object m{
val xname =...
} //Java虚拟机将其编译成带有静态方法和字段的JVM类,名为package.class
package m{
class P{
var name = xname
}
}
可以通过private[packageName]来设置可见性
引入语句的目的是使用更短的名称而不是较长的名称
引入语句可以引入包、类和对象
引入语句可以出现在任何位置,将其放入需要的地方,可以减少可能的名称冲突
引入语句可以重命名和隐藏特定成员
选取器 .{A, B}
重命名 .{A=>Z, C}
隐藏 .{A=>_, _}
java.lang、scala、Predef总是被引入,每个文件默认以以下代码开头
import java.lang._
import scala._ //可覆盖前面一个,与用户引入不同
import Predef._
8 继承
8.1 扩展类
使用extends
可以将类声明为final,这样它就不会被扩展
8.2 重写方法
重写非抽象方法必须使用override修饰符
- 避免拼写错误
- 避免新方法中使用错误的参数类型
- 在超类中引入新的方法时,可以检查是否与子类方法相冲突
调用超类方法与Java一样,使用super
8.3 类型检查和转换
使用isInstanceOf方法测试类型
if (p.isInstanceOf[Boss]) {
val s = p.asInstanceOf[Boss]
}
- p为该类或者其子类,返回true 成功
- P为null,返回false null
- P非该类或其子类,返回false 异常
判断一个对象只是某类而非子类
if (p.getClass == classOf[Boss])
使用模式匹配
p match{
case s:Boss =>
case _ =>
}
8.4 受保护字段和方法
将字段或则方法声明为protected,成员可以被任何子类访问
还提供了protected[this],访问权限制在当前对象
8.5 超类的构造
只有主构造器才可以调用超类的构造器
class A(name:string, age:Int) extends B(name, age)