Scala學習筆記

這篇文章是我跟著視頻學,再加上看博客總結(jié)的Scala關鍵知識點,用來開發(fā)Spark完全夠用。

第一節(jié):基礎

  • 變量聲明 var val
  • 七種值類型(Byte,Char,Short,Int,Long,F(xiàn)loat,Double)
  • 條件表達式
val  y = if (x>1) 1 else "error"
  • Java中所有類的基類是Object,Scala中所有類的基類是Any

  • Unit相當于Java中的void

  • for循環(huán)

1 to 10 返回 1,2,3...10
1 until 10 返回1,2,3...9

for(i <- 1  to  10){
  println(i)
}
for(i <- 1  until  10){
  println(i)
}
//for循環(huán)遍歷集合
var a = 0;
val numList = List(1,2,3,4,5,6);

// for 循環(huán)
 for( a <- numList ){
     println( "Value of a: " + a );
 }
//嵌套for循環(huán)的高級寫法&for循環(huán)過濾
for (i<- 1 to 3; j<- 1 to 3  if(i!=j)){
    println(i*10+j)
}
  • 方法和函數(shù)的聲明以及方法轉(zhuǎn)換成函數(shù)

Scala 有方法與函數(shù),二者在語義上的區(qū)別很小。Scala 方法是類的一部分,而函數(shù)是一個對象可以賦值給一個變量。換句話來說在類中定義的函數(shù)即是方法。
Scala 中使用 val 語句可以定義函數(shù),def 語句定義方法。

方法定義格式如下:

def functionName ([參數(shù)列表]) : [return type] = {
   function body
   return [expr]
}
    //聲明求兩數(shù)之和的方法:
   def addInt( a:Int, b:Int ) : Int = {
      var sum:Int = 0
      sum = a + b

      return sum
   }

函數(shù)定義格式如下:

val addInt = (x: Int,y: Int) => x + y 

第二節(jié) 數(shù)組、映射、元組、集合

  • 定長數(shù)組
val  arr1 = new Array[Int](8)

var arr2 = Array("java", "scala")
//小括號下標
println(arr2(0))
  • 變長數(shù)組
    注意要引入import scala.collection.mutable.ArrayBuffer
    然后使用ArrayBuffer 類
    追加操作: += ; ++= ; insert方法
    常用方法: reverse、delete、sum、max、min、sorted、yield關鍵字
  • 映射

創(chuàng)建映射

//用靜態(tài)類創(chuàng)建
val map1 =Map("scala"-> 1, "java"->2, "python"->3)
//用元組創(chuàng)建
val map2 = Map(("scala",1),("java",2),("python",3))

獲取值
map1("scala")

可變映射:

//必須要引用這個
import scala.collection.mutable.Map
map1("scala")=6

常用函數(shù):getOrElse

  • 元組
    K-V對的集合
val  t = ("scala", 100L,  3.14, ("spark",1))
//取元組的第一個位置的值,“scala”
t._1

note:元組的取值下標從1開始。

//這樣可以用a,b,c,d去取元組的值
val  t,(a,b,c,d) = ("scala", 100L,  3.14, ("spark",1))
println(a)

兩個方法:
toMap用于把數(shù)組(元素是元組)轉(zhuǎn)換成不可變的Map
zip拉鏈操作。用于把多個Array的值對應起來生成一個新的Array,新的Array內(nèi)容為元組。

arr1 = Array(24,25,26)
arr2 = Array("a","b","c")
arr1.zip(arr2)

如果兩個數(shù)組的長度不等,那么就會把長的數(shù)組的后面截取掉。

  • 集合

Seq(序列)

val list1 = List(1,2,3)
val list2 = 0:: list1 //在List頭部添加0
val list3 = 0 +:list1 //也是在List頭部添加0
val list6 = list1 :+ 4 //在List尾部部添加4

合并兩個List:使用++ ++: :::
可變List:使用ListBuffer

Set(集合)
集合有去重功能

import scala.collection.immutable.HashSet
val set1 = new  HashSet[Int ]()

不可變集合可以使用++合并兩個Set
可變集合使用add +=追加元素
可變集合使用 ++=合并兩個集合
可變集合使用 -= remove刪除元素

Map(映射)

val  map1 = new  HashMap[String,Int]()
map1("scala")=1
map1 +=  (("java",2))
map1. put("C++",5)
map1 - ="java"
map1 . remove("C++")

第三節(jié) 函數(shù)式編程

  • lazy關鍵字修飾的是惰性變量

  • map


  • filter


  • flatMap


  • reduce


  • fold


  • aggregate


  • 交并差集
    union、intersect、diff

  • 實現(xiàn)wordcount

object Test2{
  def main(args: Array[String]): Unit = {
    val  list = List("hello java hello scala hello python","hello java hello scala hello python")
    val words = list.flatMap(_.split(" "))
    val tuples = words.map( x=>(x,1) )
    val grouped = tuples.groupBy( (_._1) )
    val mapvalues = grouped.mapValues(_.size)
    println(grouped)
  }
}

第四節(jié) 面向?qū)ο?/h2>
  • 創(chuàng)建類、屬性
package day01


class Person{
  val id = "100"
  var name:String = _
  //只有本類才能訪問,伴生對象也可以訪問
  private var age = 120
  //只有本類才能訪問,伴生對象訪問不到
  private [this] val gender = "男"
}
//伴生對象
object Person{
  def main(args: Array[String]): Unit = {
    val  p = new Person()
    //可以修改var變量
    p.age=200
    println(p.age)
  }
}
object Test1{
  def main(args: Array[String]): Unit = {
    val p = new Person
    //p.age出錯,因為是private
    //p.age
  }
}
  • 構造器、輔助構造器

//主構造器的參數(shù)列表要放到類名的后面,和類名放在一起
//此時的faceValue:Int只能在本類調(diào)用,伴生對象也無法調(diào)用,雖然沒有用val或var修飾,但默認是val
class StructDemo(val name:String, var age:Int, faceValue:Int=90) {

  var gender:String = _

  def getFaceValue():Int={
     return faceValue
  }

  //輔助構造器
  def this(name:String,age:Int,faceValue:Int,gender:String){
    this(name,age,faceValue)  //輔助構造器第一行必須先調(diào)用主構造器
    this.gender = gender
  }
}

object StructDemo{
  def main(args: Array[String]): Unit = {
    //val s = new StructDemo("ningning",26,98)
    //println(s.faceValue)  //訪問不到,因為沒用val或var修飾
    val s = new StructDemo("ningning",26,98,"女")
    println(s.name)
    println(s.age)
    val face = s.getFaceValue()
    println(face)
    println(s.gender)

  }
}
  • 單例對象

就是用object修飾的類
創(chuàng)建對象的時候可以不用new
直接 val test = Object1
調(diào)用里面的方法直接類名.方法名即可

  • 伴生對象

與類名相同,并且用object修飾的對象叫伴生對象
類和伴生對象之間可以相互訪問私有的方法和屬性

  • apply、unapply方法

apply方法經(jīng)常用在伴生對象中,用來構造對象而不用顯式地使用new。

unapply是當做是伴生對象的apply方法的反向操作。apply方法接受構造參數(shù),然后將他們變成對象。而unapply方法接受一個對象,然后從中提取值。unapply方法返回的是一個Option.

object ScalaRunner {
  def main(args: Array[String]): Unit = {
    testApply2()
    testApplyUnApply()
    testCaseClass()
    testNumber()
    testUnapplyCheck()
  }
 
  private def testUnapplyCheck(): Unit = {
    "Hello World fdf" match {
      case Name(first, last@IsCompound()) => println(s"first: ${first}, last: ${last}")
    }
  }
 
  private def testNumber() = {
    val Number(n) = "12345"
    println(s"n: ${n}")
  }
 
 
  private def testCaseClass(): Unit = {
    val p: Person = Person("Sky", 20)
    p match {
      case Person(name, 20) => println(s"name: ${name}")
    }
  }
 
  private def testApplyUnApply() {
    val nameObj = Name("hello", "world")
    println(s"a: ${nameObj.a}, b: ${nameObj.b}")
 
    val Name(arg11, arg12) = "Hello World"
    println(s"arg11: ${arg11}, arg12: ${arg12}")
 
    val Name(arg21, arg22) = Name("hello", "world").toString
    println(s"arg21: ${arg21}, arg22: ${arg22}")
 
  }
 
  private def testApply(): Unit = {
    Name()
    (new Name) ()
    (new Name()) ()
    Name()()
 
  }
 
  private def testApply2(): Unit = {
    Name(1)
    (new Name()) (1)
    (new Name) (1)
    Name(1)(1)
  }
}
 
case class Person(val name: String, val age: Int)
 
object Number {
  def unapply(input: String): Option[Int] = {
    try{
      Some(Integer.parseInt(input.trim))
    } catch {
      case ex: NumberFormatException => None
    }
  }
 
}
 
 
class Name {
  var a = ""
  var b = ""
 
  def this(a: String, b: String){
    this
    println("Call class construct.")
    this.a = a
    this.b = b
  }
 
  def apply() = {
    println("Call class apply.")
  }
 
  def apply(i: Int) = {
    println(s"Call class apply ${i}.")
  }
 
  override def toString: String = {
    a + " " + b
  }
 
}
 
object Name {
 
  def apply(): Name = {
    println("Call object apply.")
    new Name()
  }
 
  def apply(i: Int): Name = {
    println(s"Call object apply ${i}.")
    new Name()
  }
 
  def apply(a: String, b: String): Name = {
    println(s"Call object apply.")
    new Name(a, b)
  }
 
  def unapply(input: String): Option[(String, String)] = {
    println(s"Call object unapply.")
    val pos = input.indexOf(" ")
    if(pos == -1) None
    else Some((input.substring(0, pos), input.substring(pos + 1)))
  }
 
}
 
object IsCompound {
  def unapply(input: String) = {
    println(s"Call IsCompound unapply ${input}.")
    input.contains(" ")
  }
}

運行結(jié)果:

  • private關鍵字

參考資料:Scala訪問權限修飾符:private和private[this]

作為參考和對比,首先從Java開始。
在Java中,方法可以訪問該類的所有對象的私有字段,例如:

public class Person {
    private String name;
    private int age;        // 該字段無getter/setter方法
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    private String getName() {  // 該私有方法只是為了和Scala對比
        return name;
    }
    private void setName(String name) {  // 該私有方法只是為了和Scala對比
        this.name = name;
    }
    public boolean equals(Person other) {
        return this.name.equals(other.name) && this.age == other.age;
    }
}

Person類的equals方法內(nèi),能夠直接訪問其他Person對象的私有字段(other.name/other.age)。

在Scala中,方法也可以訪問該類的所有對象的私有字段,稱為類私有字段;

但在Scala中,允許定義更加嚴格的字段訪問控制,通過private[this]來實現(xiàn),稱為對象私有字段,即只能在對象內(nèi)部訪問的字段,請看下圖:

在圖中,job屬于類私有字段,salary屬于對象私有字段;
方法內(nèi)不能訪問其他對象的對象私有字段(salary)。
Scala編譯器分別對private和private[this] 都做了什么?
首先對gao.Person.scala文件做一下修改,如下:

class Person(val name:String, var age:Int) {
  private var job = "Programmer"
  private[this] var salary = 3000F
}

然后編譯該文件,再使用javap查看編譯后的Person.class文件,可以看到:

$ javap -p target/scala-2.11/classes/gao/Person.class
Compiled from "Person.scala"
    public class gao.Person {
    private final java.lang.String name;
    private int age;
    private java.lang.String job;  // private修飾,生成的private的getter/setter方法
    private float salary;               // private修飾,沒有生成getter/setter方法
    public java.lang.String name();
    public int age();
    public void age_$eq(int);
    private java.lang.String job();                 // job字段的private的getter方法
    private void job_$eq(java.lang.String);  // job字段的private的setter方法
    public gao.Person(java.lang.String, int);
}
  • 特質(zhì)
    scala中的特質(zhì)可以類比成Java中的接口:
    1.特質(zhì)中定義的方法可以實現(xiàn),【有了大括號的就是已經(jīng)實現(xiàn)過的方法,例如下面Animal中的listen和run】;也可以不實現(xiàn)【例如Animal類中的speak方法】
package cookBook.chapter8

trait Animal{
  //沒有實現(xiàn)
  def speak

  def listen: Unit ={
  }
  def run: Unit ={
    println("I'm running")
  }
}

class People extends  Animal{
  override def speak: Unit ={
    println("I'm speaking English")
  }
}

object People extends App{
  var  people = new People
  people.speak
  people.listen//don't have result
  people.run
}

運行結(jié)果如下:

I'm speaking English
I'm running

同時,如果我們將上面類People中的speak給刪除,會顯示報錯。class People must either be declared abstract or implements abstract members...。這就是說:

2.如果一個類實現(xiàn)特質(zhì),那么必須實現(xiàn)特質(zhì)中未定義的方法。否則這個類應該為抽象類。scala中有抽象類,但是更加傾向于使用特質(zhì)。

3.scala中可以實現(xiàn)一個類同時繼承多個特質(zhì)

package cookBook.chapter8

//輪胎
trait tire{
  def run: Unit ={
    println("I can run fast")
  }
}

//方向盤
trait SteeringWheel{
  def control: Unit ={
    println("I can control the cars'direction")
  }
}

//同時繼承多個特質(zhì)
//使用with,后面可接多個with
class Roadster extends  tire with SteeringWheel {
  def display(): Unit ={
    println("I'm a Roadster")
  }
}

//敞篷跑車
object Roadster extends App{
  var roadster = new Roadster
  roadster.display()
  roadster.run
  roadster.control
}

運行結(jié)果如下:

I'm a Roadster
I can run fast
I can control the cars'direction

注:特質(zhì)構造順序
1、首先調(diào)用超類的構造器

2、然后調(diào)用特質(zhì)構造器,特質(zhì)構造器在超類構造器之后,類構造器之前

3、特質(zhì)由左到右被構造

4、在每個特質(zhì)當中,父特質(zhì)先被構造

5、如果多個特質(zhì)公用一個父特質(zhì),而那個父特質(zhì)已經(jīng)被構造過了,則不會再被構造

6、所有特質(zhì)構造完畢,子類被構造

介紹特質(zhì)比較詳細的一篇文章https://www.cnblogs.com/nowgood/p/scalatrait.html

  • 抽象類
//抽象類
abstract class Animal1{
  //抽象字段
  var name:String 
  var size:Int
  
  //抽象方法
  def walk
}
 
//抽象類實現(xiàn)類
class Cat(var length:Int)extends Animal1{
  override var name = "cat"
  override var size = 100
  override def walk{
    println(this.name + ":" + this.size + ":" + this.length)
  }
  
}
object AbstractClassTest {
  
  def main(args: Array[String]): Unit = {
      val cat = new Cat(200)
      cat.walk
      println("name:" + cat.name)
      println("size:" + cat.size)
      println("length:" + cat.length)
  }
}

cat:100:200
name:cat
size:100
length:200

Scala抽象類不能被實例化,包含若干定義不完全的方法,具體的實現(xiàn)由子類去實現(xiàn)。

第五章 模式匹配

以下部分來自:菜鳥教程
Scala 提供了強大的模式匹配機制,應用也非常廣泛。
一個模式匹配包含了一系列備選項,每個都開始于關鍵字 case。每個備選項都包含了一個模式及一到多個表達式。箭頭符號 => 隔開了模式和表達式。以下是一個簡單的整型值模式匹配實例:

object Test {
   def main(args: Array[String]) {
      println(matchTest(3))

   }
   def matchTest(x: Int): String = x match {
      case 1 => "one"
      case 2 => "two"
      case _ => "many"
   }
}

執(zhí)行以上代碼,輸出結(jié)果為:

$ scalac Test.scala
$ scala Test
many

match 對應 Java 里的 switch,但是寫在選擇器表達式之后。即: 選擇器 match {備選項}。match 表達式通過以代碼編寫的先后次序嘗試每個模式來完成計算,只要發(fā)現(xiàn)有一個匹配的case,剩下的case不會繼續(xù)匹配。接下來我們來看一個不同數(shù)據(jù)類型的模式匹配:

object Test {
   def main(args: Array[String]) {
      println(matchTest("two"))
      println(matchTest("test"))
      println(matchTest(1))
      println(matchTest(6))

   }
   def matchTest(x: Any): Any = x match {
      case 1 => "one"
      case "two" => 2
      case y: Int => "scala.Int"
      case _ => "many"
   }
}

執(zhí)行以上代碼,輸出結(jié)果為:

$ scalac Test.scala
$ scala Test
2
many
one
scala.Int

實例中第一個 case 對應整型數(shù)值 1,第二個 case 對應字符串值 two,第三個 case 對應類型模式,用于判斷傳入的值是否為整型,相比使用isInstanceOf來判斷類型,使用模式匹配更好。第四個 case 表示默認的全匹配備選項,即沒有找到其他匹配時的匹配項,類似 switch 中的 default。

  • 樣例類

使用了case關鍵字的類定義就是就是樣例類(case classes),樣例類是種特殊的類,經(jīng)過優(yōu)化以用于模式匹配。
以下是樣例類的簡單實例:

object Test {
   def main(args: Array[String]) {
       val alice = new Person("Alice", 25)
    val bob = new Person("Bob", 32)
       val charlie = new Person("Charlie", 32)
   
    for (person <- List(alice, bob, charlie)) {
        person match {
            case Person("Alice", 25) => println("Hi Alice!")
            case Person("Bob", 32) => println("Hi Bob!")
            case Person(name, age) =>
               println("Age: " + age + " year, name: " + name + "?")
         }
      }
   }
   // 樣例類
   case class Person(name: String, age: Int)
}

執(zhí)行以上代碼,輸出結(jié)果為:

$ scalac Test.scala
$ scala Test
Hi Alice!
Hi Bob!
Age: 32 year, name: Charlie?

在聲明樣例類時,下面的過程自動發(fā)生了:
構造器的每個參數(shù)都成為val,除非顯式被聲明為var,但是并不推薦這么做;
在伴生對象中提供了apply方法,所以可以不使用new關鍵字就可構建對象;
提供unapply方法使模式匹配可以工作;
生成toString、equals、hashCode和copy方法,除非顯示給出這些方法的定義。

  • 偏函數(shù)

第六節(jié) 柯里化、隱式轉(zhuǎn)換

有關逆變協(xié)變
https://blog.csdn.net/zero__007/article/details/52245475
這篇文章是講Java的。不過意思一樣。
文章中提到了PECS原則,就是什么時候使用逆變協(xié)變的。

第七節(jié) Actor和AKKA

Scala筆記整理(九):Actor和AKKA

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容