Scala
Scala特点:
- 和 Java 无缝整合,满足各自语法基础上调用 Java 库;
- 类型推断,类似于 Golang,Scala 通过
val
声明常量,通过var
声明变量。 - 支持并行和分布式;
- 高阶函数编程,可以理解为面向对象编程,但是函数可以作为对象并当作参数传入。
数据类型
- Null:代表空值,是 AnyRef 的子类;
- Nothing:所有类型的子类,用于类型推断时不确定类型定义;
- Unit:无返回值的函数的类型,对标 void;
- Nil:长度为 0 的 List;
使用规则:
- 区别 Object、Class,Object 可以理解为 Java代码中的单例(工具类),Class 理解为普通的类,程序加载时会预先加载 Object 中的方法;
- Scala 中定义变量使用 var,定义常量使用 val,定义变量和常量不可以省略标识符,但是类型可以省略,系统会自动推断;
- Scala 中每行后面会有分号自动推断机制;
- Scala 中可以传参,传参必须指定类型,有了参数就会自动生成默认构造函数,类中地属性默认会有 setter、getter 方法;
- 类中重写构造时,重写构造函数定义格式为
def this(....) {....}
,函数体第一行必须调用默认构造; - Scala 中当使用
new 对象
时,类中除了方法不执行【除了构造方法外】,其他方法都执行。 - 在同一个 scala 文件中,class 名称和 Object 名称一样时,这个类叫做对象的伴生类,这个对象叫做这个类的伴生对象,之间可以相互访问私有变量。
- 方法定义时:
- 如果最后使用 return,那么方法返回值类型一定要指定;如果方法体中没有 return,默认将方法中最后一行结果返回,此时可以省略方法返回类型,会自动进行推断;
- 定义方法时,如果省略了方法名称和方法体之间的 “=”,那么无论方法计算的结果是什么,都会丢弃返回空。
- 递归方法需要显式地声明返回值类型。
特殊函数类型
匿名函数
函数可以看作变量,类似于 Golang 中函数使用方式。
def fun3(s:String*): Unit = {
s.foreach(er => {
println(er)
})
}
def funObj = (a:Int, b:Int)=>{
a+b
}
偏应用函数
某些场景下,函数有很多个参数而且会被调用很多次,为了避免调用函数时重复写无关参数,可以定义偏应用函数,定义时经常变化的参数位置用 _:type
占位。
def log(date:Date, s:String) = {
println(s"data is $date, log is $s")
}
val date = new Date()
val logWithDate = log(date, _:String)
logWithDate("log1")
logWithDate("log2")
logWithDate("log3")
高阶函数
- 将函数作为参数,传入数据的同时传入处理逻辑,在类似 Spark 处理过程中的使用模式:
def fun4(f:(Int, Int)=>Int, s:String): String = {
val i = f(100, 200)
i + "#" + s
}
val nRes = fun4((a:Int, b:Int)=>{a*b}, "scala")
println(nRes)
- 函数作为返回值,必须显示定义返回类型:
def fun5(s:String): (String, String)=>String = {
def funInner(s1:String, s2:String): String ={
s1 + "#" + "s2"
}
funInner
}
- 函数参数和返回值均为函数类型:
def fun6(f:(Int, Int)=>Int): (String, String)=>String = {
val i = f(1,2)
def fun1(s1:String, s2:String): String = {
s1 + "@" + s2 + "*" + i
}
fun1
}
字符串和集合
数组——Array
注意 Array
是不可变的,ArrayBuffer
是可变的。
object Lesson_Obj3 {
def main(args: Array[String]): Unit = {
// 定义并初始化赋值
val arr1 = Array[String]("1", "2", "3")
val arr2 = Array[String]("101", "102", "103")
// 定义二维数组
val arr3 = new Array[Array[String]](3)
arr3(0) = Array[String]("1", "2", "3")
arr3(1) = Array[String]("1", "2", "3")
arr3(2) = Array[String]("1", "2", "3")
for (outer <- arr3; elem <- outer) {
println(elem)
}
arr3.foreach(elem=>{elem.foreach(println)})
// 拼接两个数组
val newArr = Array.concat(arr2, arr1)
newArr.foreach(println)
// 填充生成新数组
Array.fill(4)("hello")
// 生成可变的 ArrayBuffer
val arrbuf = new ArrayBuffer[Int]()
arrbuf.+=(1)
arrbuf.+=:(100)
arrbuf.foreach(println)
arrbuf.append(5, 6, 7)
}
}
集合——List
注意 List
是不可变的,ListBuffer
是可变的。List
支持 map()/count()/filter()/flatMap()
等方法来处理集合。
object Lesson_Obj4 {
def main(args: Array[String]): Unit = {
// 定义集合并初始化,默认元素类型是 Any
val list = List(1, 2,3,"A", true)
list.foreach(println)
val list2 = List[String]("str1 1", "str2 2", "str3 3")
val result = list.+("str4")
println(result)
val list3 = list2.map(s => {
s.split(" ")
})
list3.foreach(arr => {
arr.foreach(println)
})
val list4 = list2.flatMap(s => {s.split(" ")})
list4.foreach(println)
val list5 = list2.filter(s => {
s.equals("str3 3")
})
list5.foreach(println)
val num = list2.count(s => {
s.length > 4
})
// 可变List
val changeList = ListBuffer[String]("change1", "change2", "change3")
changeList.append("change4")
}
}
唯一集合——Set
注意集合的可变与不可变,默认是不可变集合,如果要使用可变集合,在定义时需要这样声明 mutable.Set
。
object Lesson_Obj5 {
def main(args: Array[String]): Unit = {
val set1 = Set[Int](1, 2, 1, 3, 4)
set1.foreach(println)
for (elem <- set1) {
println(elem)
}
val set2 = Set[Int](4,5,6,1,2)
val res1 = set2.intersect(set1)
res1.foreach(println)
val res2 = set2.diff(set1)
res2.foreach(println)
val res3 = set1 & set2
// 可变长Set
import scala.collection.mutable.Set
val set3 = Set[Int](1,2,3,4)
set3.+= (100)
set3.+= (101, 102, 103)
set3.foreach(println)
val set4 = mutable.Set[Int](1,2,3,4,5,6)
}
}
键值对集合——Map
Map 同上也区分可变不可变。
object Lesson_Obj6 {
def main(args: Array[String]): Unit = {
val map = Map[String, Int]("a"->100, "b"->101, "c"->102)
map.foreach(println)
val option = map.get("a")
println(option)
val option1 = map.get("aa")
println(option1)
val value = map.get("a").get
println(value)
val defaultVal = map.get("a").getOrElse("no value")
println(defaultVal)
// 根据Keys 获取 Value
val keys = map.keys
keys.foreach(key => {
val tmp = map.get(key).get
println(s"key = $key, value = $tmp")
})
// 操作 Values
val values = map.values
values.foreach(println)
val map2 = Map[String, Int](("a", 1), ("b", 2), ("c", 3))
map2.foreach(println)
// 合并 Map
val map3 = Map[String, Int](("a", 100), ("d", 200), ("c", 300))
map3.foreach(println)
val map4 = map2.concat(map3)
map4.foreach(println)
// 可变 Map
val changeMap = mutable.Map[String, Int](("a", 1), ("b", 2), ("c", 3))
changeMap.foreach(println)
// 过滤 Map
val map5 = changeMap.filter(kv => {
val key = kv._1
val values = kv._2
value == 2
})
map5.foreach(println)
}
}
元组——tuple
元组类似于 List,只不过元素默认为任意值。
object Lesson_Obj6 {
def main(args: Array[String]): Unit = {
val tuple = Tuple1[String]("hello")
val tuple2 = new Tuple2("a", 100)
val tuple3 = Tuple3(1, true , "c")
val tuple4 = Tuple4(1, 3, 4, "a")
val tuple5 = Tuple5(1, 1, 1, 1, "2")
// 遍历元组
val iterator = tuple5.productIterator
while (iterator.hasNext) {
println(iterator.next())
}
iterator.foreach(println)
}
}
Trait
Trait 可以理解为接口或者抽象类,Trait 中的方法可以实现或者不实现。其他类继承 Trait 时要注意实现相应方法。
object Lesson_Obj7 {
def main(args: Array[String]): Unit = {
val p = new Human()
p.read("张三")
p.listen("李四")
val p1 = new Point(1, 2)
val p2 = new Point(3, 4)
}
}
trait Read {
def read(name:String) = {
println(s"$name is reading")
}
}
trait Listen {
def listen(name:String) = {
println(s"$name is listening")
}
}
class Human() extends Read with Listen{
}
class Point(x:Int, y:Int) extends isEqual {
val xx = x
val yy = y
// 重写 isEqual 方法
override def equal(o: Any): Boolean = {
o.isInstanceOf[Point] && o.asInstanceOf[Point].x == this.x
}
}
// trait 定义方法但是未实现
trait isEqual {
def equal(o:Any): Boolean
def notEqual(o:Any): Boolean = !equal(o)
}
模式匹配
Scala 中 Match 匹配:
Math
理解为 Swtich,其中case_
相当于 default,放在最后;
object Lesson_Obj8 {
def main(args: Array[String]): Unit = {
val tp = (1, 2, 3, "a", "abc", true)
val iter = tp.productIterator
iter.foreach(MatchTest)
}
def MatchTest(o:Any) = {
o match {
case 1 => println("value is 1")
case i:Int => println("type is Int")
case d:Double => println("type is Double")
case s:String => println("type is String")
case 'c' => println("type is Char")
case _ => println("no match...")
}
}
}
偏函数,只能用于匹配一个值并且在匹配上了返回某个值:
def partialMatch :PartialFunction[String, Int] = {
case "abc" => 2
case "a" => 1
case _ => 200
}
val result = partialMatch("abc")
隐式值与隐式转换函数
隐式值
隐式值是指在定义参数时在前面加上 implicit
,方法中部分参数通过 implicit
来修饰。隐式值的作用就是使用函数时,不必手动传入部分隐式值参数,Scala 会自动在作用域范围内查找隐式值并自动传入。
// 函数参数使用隐式参数
def PartialFunction2(age:Int)(implicit name:String): Unit = {
println(s"name is $name, age is $age")
}
def main(args: Array[String]): Unit = {
val r = new Rabbit("rabbit")
r.canFly()
// 定义隐式值
implicit val name = "zhagnsan"
PartialFunction2(100)
}
注意:
- 同类型的参数的隐式值只能在作用域内出现一次,同一个作用于不能定义多个隐式值;
- 如果函数带有多个参数,部分参数定义为隐式值,那么这些参数需要单独
implicit
定义; - 一个方法只有一个参数是隐式转换参数时,可以直接定义
implicit
关键字修饰的参数,调用时直接创建类型不传入参数即可; - 一个方法如果有多个参数,其中部分是隐式参数,此时隐式关键字必须出现在后面,且只能出现一次。
隐式转换函数
隐式转换函数即通过 implicit
修饰的函数,作用于一些特定的使用场景。比如类A 想要调用 类B 的方法,不能直接调用,此时如果定义了隐式转换函数,系统会在调用方法时自动查找能否转换。
object Lesson_Obj9 {
// 隐式转换函数
implicit def RabbitToAnimal(r:Rabbit): Animal = {
new Animal(r.name)
}
def main(args: Array[String]): Unit = {
// Rabbit 类对象想要调用 Animal 类的方法,不可以直接调用
val r = new Rabbit("rabbit")
r.canFly()
}
}
class Rabbit(xname:String) {
val name = xname
}
class Animal (xname:String){
val name = xname
def canFly() = {
println(s"$name can fly")
}
}
Actor Model
Actor Model
是用来编写并行计算或者分布式系统的高层次抽象,避免程序员为多线程编程下共享锁问题影响。