Scala

1、特性


统一类型

Unified-Types.png

类型推断

可以使用 varval 定义变量,编译器根据赋值自行推断变量数据类型。

var 用来声明变量,val 用来声明常量,类似 Java 中 final 修饰的变量。

var a = 1;
val b = "123";
// 可以指定类型
var c:Int = 1;

函数式

Scala 是函数式编程语言,因此所有方法都必须有返回值。Java 中 void 类型返回值,在 Scala 中使用 Uint 声明。

def tunit(i: Int): Unit = { }

主方法

主方法必须声明在 object 中。一个 Scala 文件种,可以声明多个 objectclass,如果多个 object 中都具有 main 函数,则运行时需要指定启动 mainobject 对象。

object Main {
  def main(args: Array[String]): Unit = {
    println("run")
  }
}

CLASS & OBJECT

object 中声明成员成员和函数,类似于 Java 中 static 声明的静态成员和方法。

class 声明的对象,类似于 Java 中非静态成员和方法,必须通过 new 对象实例才可访问。

如果 objectclass 同名,会产生伴生关系,伴生关系中 class 可以访问 object 中似有成员和函数。

裸露代码

object 中,在任何函数外的代码,类似于 Java 中 static { } 代码块中代码,会先于 main 函数执行。

object Main {
  println("before main")
  def main(args: Array[String]): Unit = {
    println("main run")
  }
  println("after main")
}
// 输出顺序:
// before main
// after main
// main run

class 中,裸露代码,会被规整至类似于 Java 中无参构造函数中。

object Boot {
  def main(args: Array[String]): Unit = {
    new Boot boot()
  }
}
class Boot {
  println("before boot")
  def boot(): Unit = {
    println("boot run")
  }
  println("after boot")
}
// 输出顺序:
// before boot
// after boot
// boot run

类名构造器

类名构造器中参数,默认是 val 的常量,new 实例时必须赋值,也可以声明成 var 类型:class Boot(var sex: String) {}

class Boot(sex: String) {

  private var name: String = _

  def this(sex: String, name: String) {
    this(sex)
    this.name = name
  }
}

2、流控


IF 条件控制

var a = 22;
if (a <= 0) {
    println("if branch")
} else if(a < 100) {
    println("else if branch")
} else {
    println("else branch")
}

WHILE LOOP

var w = 0;
while (w < 10) {
    println(s"while loop -> $w")
    // Scala 不允许使用 w++ 或 ++w 语法
    w += 1;
}

RANGE

// 声明包含 1~10 的 Range 集合
var seqs: Range.Inclusive = 1 to 10
seqs.foreach(println)

// 通过步进为2 声明包含 1,3,5,7,9 的 Range 集合
var steps: Range.Inclusive = 1 to (10, 2)
steps.foreach(println)

// 声明包含 1~9 的 Range 集合
var untils = 1 to 10
untils.foreach(println)

FOR LOOP

var seqs = 0 to 10
for (i <- seqs) {
    println(i)
}

// 循环逻辑 与 业务逻辑 分离
for (i <- seqs if (i % 2 == 0)) {
    println(i)
}

嵌套循环

for (i <- 1 to 9) {
    for (j <- 1 to i) {
        print(s"${j}X${i}=${j * i}\t")
    }
    println()
}
// 可以简化为
for (i <- 1 to 9; j <- 1 to i) {
    print(s"${j}X${i}=${j * i}\t")
    if (j == i) println()
}

变量收集:使用 yield 关键字,代码块中最后一行数据,会被收集到结果集中。

var seq: IndexedSeq[Int] = for (i <- 1 to 10) yield {
    print(s"$i ")
    i * 2
}
println
println(seq)
// 输出
// 1 2 3 4 5 6 7 8 9 10 
// Vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)

3、函数


返回值类型推断

  • 不定义返回值类型,默认是 Unit 类型。
def test() = { }
  • 如果函数包含 return,则返回类型自动推断为 return 对象对应类型。
def test() = {
  var a:Int = 3
  var b:Int = a * 2
  // b * 3 为返回值 
  // 因为没有显示声明 return 所以函数返回值类型启用类型推断
  b * 3
}
  • 如果声明了 return 关键字,则函数必须声明返回值类型。
def test(): Int = {
  return 1
}

参数

函数参数必须声明类型,且参数全部是 val 类型的,不可变更。

// Recursive 递归函数必须声明返回值类型
def func(num: Int): Int = {
  if (num == 1)
    num
  else
    num * func(num - 1)
}

函数赋值到变量

def func(): Unit = {
    println("func invoke")
}

var method = func _

默认值函数

def main(args: Array[String]): Unit = {
  func()
  func(22)
  func(name = "Arthur")
  func(22, "Arthur")
}

def func(age: Int = 18, name: String = "Andy"): Unit = {
  println(s"age: $age, name: $name")
}

可变参数

def func(a: Int*): Unit = {
  println(s"length -> ${a.length}")
  // 迭代遍历方式
  a.foreach((x: Int) => { print(s"$x\t") })
  a.foreach((x: Int) => print(s"$x\t"))
  a.foreach(print(_))
  a.foreach(print)
  println
}

func()
func(1)
func(1, 2, 3, 4, 5)

匿名函数

def main(args: Array[String]): Unit = {
  var func = (a: Int, b: Int) => {
    a + b
  }
  println(func(3, 4))

  var tfunc: (Int, Int) => Int = (a, b) => {
    a + b
  }
  println(tfunc(3, 4))
}

嵌套函数

如果函数重名,则调用时优先寻找作用域最近的,且外部函数定义的变量对内部函数可见。

def main(args: Array[String]): Unit = {
  var x = "x"
  def tfunc: (String) => Unit = (a) => {
    println(s"inner -> $a $x")
  }
  tfunc("arthur")
}

def tfunc(a: String): Unit = {
  println(s"outer -> $a")
}
// 输出 inner -> arthur x

偏函数

var pfunc:PartialFunction[Any, String] = {
  case "hello" => "value is hello"
  case o: Int => s"value is int with $o"
  case _ => "none"
}
println(pfunc("hello"))
println(pfunc(88))
println(pfunc("great"))

偏应用函数

var formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")

def main(args: Array[String]): Unit = {
  var info = log(_: Date, "info", _: String);
  var debug = log(_: Date, "debug", _: String);

  info(new Date, "ok")
  debug(new Date, "debug log")
}

def log(date: Date, types: String, msg: String): Unit = {
  println(s"${formatter.format(date)} - $types :> $msg")
}

高阶函数 - 函数作为参数

def compute(a: Int, b: Int, f: (Int, Int) => Int): Unit = {
  var res: Int = f(a, b);
  println(res);
}
compute(3, 8, (x: Int, y: Int) => x + y)
  • 如果参数在匿名函数中,会顺序使用,且仅适用一次,可简化语法为 _+_
compute(3, 8, _ + _)  
compute(3, 8, _ * _)

高阶函数 - 函数作为返回值

def compute(a: Int, b: Int, i: String): Unit = {
  var res: Int = factory(i)(a, b);
  println(res);
}

def factory(i: String): (Int, Int) => Int = {
  def add(x: Int, y: Int): Int = { x + y }
  def sub(x: Int, y: Int): Int = { x - y }

  if ("+".equals(i)) {
    add
  } else if ("-".equals(i)) {
    sub
  } else if ("*".equals(i)) {
    (x: Int, y: Int) => { x * y }
  } else if ("/".equals(i)) {
    (x: Int, y: Int) => x / y
  } else {
    (x: Int, y: Int) => 0
  }
}

compute(3, 8, "+")
compute(3, 8, "/")

柯里化

  def currying(a:Int*)(b:String*): Unit = {
    a.foreach(println)
    b.foreach(println)
  }
  currying(1, 2, 3)("a", "b", "c")

4、集合容器


Scala mutable 包下是可变集合容器,immutable 包下是不可变集合容器。

数组

Scala 中,由于 [] 被泛型占用了,因此使用 () 定位数组下标。

val array = Array[Int](1, 2, 3, 4)
array(0) = 3
array.foreach(println)

List集合

  • 不可变集合:
 val list = List(1, 2, 3, 4, 5, 4, 3, 2, 1)
 list.foreach(println)
  • 可变集合:
val listbuf = ListBuffer[Int]()
listbuf.+=(1)
listbuf.+=(2)
listbuf.+=(3)
listbuf.foreach(println)

Set集合

  • 不可变Set:
import scala.collection.immutable.Set
val set: Set[Int] = Set(1, 2, 3, 4, 3, 2, 1)
set.foreach(println)
  • 可变Set:
import scala.collection.mutable.Set
val mutset = Set(1, 2, 3, 4)
mutset.add(8)
mutset.foreach(println)

Tuple

val tuple2 = new Tuple2[Int, String](11, "abc")
val tuple3 = Tuple3(11, "abc", 's')
val tuple4 = ((i: Int) => i + 8, 2, 3, 4, "aaa")

println(tuple2._2)
println(tuple4._4)
var x = tuple4._1(3)
println(x)

val tIter = tuple4.productIterator
while(tIter.hasNext) {
    println(tIter.next())
}

Map

  • 不可变Map:
import scala.collection.immutable.Map
var map: Map[String, Int] = Map(("a", 1), "b" -> 2, ("c", 3), ("a", 4))

val oa:Option[Int] = map.get("w").orElse(Some(0))
println(oa)

val ob = map.getOrElse("w", 0)
println(ob)

val keys:Iterable[String] = map.keys
for (key <- keys) {
    println(s"key: ${key}, value: ${map.get(key).get}")
}
  • 可变Map:
import scala.collection.mutable.Map
val map:Map[String, Int] = Map(("a", 1), ("b", 2))
map.put("c", 3)
map.remove("c")

符号方法

 val list = List(1, 2, 3)
  • :::用于的是向队列头部追加数据,产生新的列表。
var nlist = 4 :: list
println(nlist)
// 输出: List(4, 1, 2, 3)
  • .:::与 :: 作用相同。
var nlist = list.::(5)
println(nlist)
// 输出: List(5, 1, 2, 3)
  • :+:用于在队列尾部追加元素。
var nlist = list :+ 6
println(nlist)
// 输出: List(1, 2, 3, 6)
  • +:: 用于在队列头部添加元素。
val nlist = "A" +: "B" +: Nil
// Nil 是一个空的 List 定义为 List[Nothing]
println(nlist)
// 输出: List(A, B)
  • ::::用于连接两个List类型的集合。
val list2 = List("A", "B")
var nlist = list ::: list2
println(nlist)
// 输出: List(1, 2, 3, A, B)
  • ++:用于连接两个集合。
val list2 = List("A", "B")
var nlist = list ++ list2
println(nlist)
// 输出: List(1, 2, 3, A, B

5、Trait


object Main {

  def main(args: Array[String]): Unit = {
    var person = new Person("Andy")
    person.hello()
    person.say()
    person.kill()
    person.love()
    person.pray("bless me")

    var god:God = new Person("God")
    god.pray("is god")
  }

}

trait God {
  def say(): Unit = {
    println("god say")
  }

  def pray(info: String): Unit
}

trait Devil {
  def kill(): Unit = {
    println("devil kill")
  }
}

trait Angel {
  def love(): Unit = {
    println("angel love")
  }
}

class Person(name: String) extends God with Devil with Angel {
  def hello(): Unit = {
    println(s"${this.name} hello")
  }

  override def pray(info: String): Unit = {
    println(s"god $info")
  }
}

6、样例类


object Main {

  def main(args: Array[String]): Unit = {
    // 样例类不需要 new 关键字
    var dog1 = Dog(3, "hsq");
    var dog2 = Dog(3, "hsq");

    println(dog1.equals(dog2))
    // 输出 true
    println(dog1 == dog2)
    // 输出 true
  }

}

case class Dog(age: Int, name: String) {

}

7、MATCH


val tuple: (Double, Int, String, Boolean, Char) = (1.0, 3, "ok", true, 'c')
val iter: Iterator[Any] = tuple.productIterator

val mapped = iter.map((x) => {
  x match {
    case 1 => s"$x is 1"
    case 3 => s"$x is 3"
    case true => s"$x is true"
    case o: Char if o == 'c' => s"$o is Char with $x"
    case _ => s"$x with default"
  }
})

while (mapped.hasNext) println(mapped.next())

8、隐式转换


Scala 编译器发现 list.foreach(println) 找不到方法链接,则开始寻找 implicit 定义的参数类型与 list 一致的方法。

如果找到,则在编译器,将代码改写为 new EnhanceLinkedList[T](list).foreach(println)

object Main {

  def main(args: Array[String]): Unit = {
    var list = new util.LinkedList[Integer]()
    list.add(1)
    list.add(2)
    list.add(3)

    implicit def enhance[T](list: util.LinkedList[T]): EnhanceLinkedList[T] = {
      new EnhanceLinkedList[T](list)
    }

    list.foreach(println)
  }

}

class EnhanceLinkedList[T](list: java.util.LinkedList[T]) {
  def foreach(f: (T) => Unit): Unit = {
    val iter = this.list.iterator()
    while (iter.hasNext) f(iter.next())
  }
}

也可通过隐式类定义,但隐式类必须为内部类。

import java.util

object Main {

  def main(args: Array[String]): Unit = {
    var list = new util.LinkedList[Integer]()
    list.add(1)
    list.add(2)
    list.add(3)

    list.foreach(println)
  }

  implicit class EnhanceLinkedList[T](list: util.LinkedList[T]) {
    def foreach(f: (T) => Unit): Unit = {
      val iter = this.list.iterator()
      while (iter.hasNext) f(iter.next())
    }
  }

}

隐式参数

如果参数列表使用 implicit 修饰,则所有参数都需要在外部定义,才可使用隐式传参,且相同类型参数不可定义多个。

def main(args: Array[String]): Unit = {

  implicit var name: String = "Andy"
  implicit var age: Int = 18

  def method(implicit name: String, age: Int): Unit = {
    println(s"name: $name, age: $age")
  }

  method("Hudy", 22)
  method

}

9、Spark


Word Counter

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

object Main {

  def main(args: Array[String]): Unit = {
    val conf = new SparkConf()
    conf.setAppName("WordCount")
    conf.setMaster("local")

    val sc = new SparkContext(conf)
    
    // val rdd: RDD[String] = sc.textFile("data/testdata.txt")
    // val words: RDD[String] = rdd.flatMap((x: String) => { x.split(" ") })
    // val pairwords: RDD[(String, Int)] = words.map((s: String) => (s, 1))
    // val result: RDD[(String, Int)] = pairwords.reduceByKey(_ + _)

    val result: RDD[(String, Int)] = sc.textFile("data/testdata.txt")
      .flatMap(_.split(" ")).map((_, 1)).reduceByKey(_ + _)
    result.foreach(println)
  }

}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。