Scala05

伴生對象 vs 伴生類

Scala中的伴生對象
對於Object內部的方法,我們可以直接通過Object.method(對象名.方法)來調用
類似於Java中的static
舉例:現在object裏面有個increment方法

object Timer {
  var count = 0

  def increment():Long = {
    count += 1
    count
  }
}

在scala的shell裏運行:

scala> object Timer {
     |   var count = 0
     |   def increment():Long = {
     |     count += 1
     |     count
     |   }
     | }
defined object Timer

scala> Timer.increment
res33: Long = 1

scala> Timer.increment
res34: Long = 2

scala> Timer.increment
res35: Long = 3

伴生對象 和伴生類 是相輔相成的
class A
object A
class A 叫做object A 的伴生類
object A 叫做class A 的伴生對象

object ApplyDemo {
  def main(args: Array[String]): Unit = {
    for(i <- 1 to 10){
      ApplyTest.increment
    }
    println(ApplyTest.count)
  }
}
class ApplyTest{

}
object ApplyTest{
  println("object ApplyTest enter...")
  var count = 0

  def increment = {
    count = count + 1
    count
  }

  println("object ApplyTest leave...")
}

輸出結果:

object ApplyTest enter...
object ApplyTest leave...
10

Process finished with exit code 0

從上面可以看出,你調用object裏的方法,會觸發它把object裏所有的東西先執行一遍,方法不調用不執行(increment 只是定義了,沒有調用),在for裏面觸發了,每次把它加1,所以看到的是先打印兩句話,然後把count打印出來。
有點想不明白,for循環裏循環10次,不應該觸發10次object的方法嗎,爲什麼不把那兩句話打印10次?

上面代碼變一下:

object ApplyDemo {
  def main(args: Array[String]): Unit = {
    ApplyTest.static
  }
}

object ApplyTest{
  println("object ApplyTest enter...")
  
def static: Unit = {
    println("object ApplyTest static")
  }

  println("object ApplyTest leave...")
}

輸出結果:

object ApplyTest enter...
object ApplyTest leave...
object ApplyTest static

Process finished with exit code 0

調用object.static方法,觸發object裏所有東西執行一遍,方法不調用不執行,先打印object裏的兩句話,然後,運行那個static方法,有了第三行。
再升級:

package com.ruozedata.bigdata.scala04
object ApplyDemo {
  def main(args: Array[String]): Unit = {
    val a = new ApplyTest()    //new 一個 ApplyTest這個類  類要用的時候先new出來
    println(a)       //這個println其實調用的是toString方法,包名+類名@hashcode
  }
}

class ApplyTest{
}

object ApplyTest{
  println("object ApplyTest enter...")

  def static: Unit = {
    println("object ApplyTest static")
  }

  println("object ApplyTest leave...")
}

輸出結果:

//因爲調用的是toString方法,就是包名+類名@hashcode   所以是下面的:
com.ruozedata.bigdata.scala04.ApplyTest@1b604f19

Process finished with exit code 0

再變一下:

package com.ruozedata.bigdata.scala04

object ApplyDemo {
  def main(args: Array[String]): Unit = {
    val a = new ApplyTest()
    println(a)
    a.method()    //調用a裏的method方法
  }
}
class ApplyTest{
  def method(): Unit ={
    println("class ApplyTest method")
  }
}
object ApplyTest{
  println("object ApplyTest enter...")
  def static: Unit = {
    println("object ApplyTest static")
  }

輸出結果:

com.ruozedata.bigdata.scala04.ApplyTest@1b604f19
class ApplyTest method

Process finished with exit code 0

再擴展一下,下面這個很重要

package com.ruozedata.bigdata.scala04

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

	//平時我們都是new的
    //現在沒有new,直接=類名()   其實調用的是object對象裏面的apply方法
    //apply方法被觸發,觸發之後再new出來
    val b = ApplyTest()  //沒有new    
    b.method()
  }
}

class ApplyTest{
  def method(): Unit ={
    println("class ApplyTest method")
  }
  def apply() ={
    println("class apply method invoked...")
  }
}

object ApplyTest{
  println("object ApplyTest enter...")
  var count = 0

  def apply() ={           //apply它底層返回的是下面new出來的ApplyTest這個類
    println("object apply method invoked...")
    new ApplyTest()      //在這裏new 一個ApplyTest類出來
  }

  println("object ApplyTest leave...")
}

輸出結果:

object ApplyTest enter...
object ApplyTest leave...
object apply method invoked...
class ApplyTest method

Process finished with exit code 0

記住:
遇到 xxx = 類名() 一定是調用object對象裏的apply方法,然後new出來

還有:

object ApplyDemo {
  def main(args: Array[String]): Unit = {
    val c = new ApplyTest()
    println( c )
    println(c())    //c是一個引用,c()調用的是Class的apply方法,不常用
  }
}
class ApplyTest{
  def method(): Unit ={
    println("class ApplyTest method")
  }
  def apply() ={
    println("class apply method invoked...")
  }
}


object ApplyTest{
  println("object ApplyTest enter...")

  def apply() ={  //apply它底層返回的是下面new出來的ApplyTest這個類
    println("object apply method invoked...")
    new ApplyTest()  //在這裏new 一個ApplyTest類出來
  }

  println("object ApplyTest leave...")
}

輸出結果:

com.ruozedata.bigdata.scala04.ApplyTest@1b604f19
class apply method invoked...

Process finished with exit code 0

記住兩句話:
①遇到 xxx = 類名() 一定是調用object對象裏的apply方法,然後new出來。(在apply裏做了什麼?new 一個,然後最後一行返回)
②如果是對象/引用(就是上面new出來的),它調用的是Class.apply方法
舉例:
在創建一個數組的時候,
var array = Array(“a”,“b”,“c”)
點擊Array,可以看到它調用的就是object對象裏的apply方法
在這裏插入圖片描述

把一個函數賦給一個變量

object FunctionAdApp {
  def main(args: Array[String]): Unit = {
    val sayHelloFunc = sayHello _    //就是這樣寫的,空格加一個下劃線
    println(sayHelloFunc("zhangsan"))
  }

  def sayHello(name:String)={
    println("hello : " + name)
  }
}

匿名函數

匿名函數(參數名:參數類型)=>函數體

 //定義了一個匿名函數,並把匿名函數賦值給了一個變量(也可以賦值給一個函數)
object FunctionNoNameApp {
  def main(args: Array[String]): Unit = {
    val add = (x:Int,y:Int) =>{  x+y  }
    println(add(2,3))
  }
}

函數的克里化currying

spark的udf函數裏面經常會用到

object FunctionCurryingApp {
  def main(args: Array[String]): Unit = {
    println(sum(12,2))
    println(add(12)(2))    //調用
  }
  
  def sum(x:Int,y:Int)= x+y    //正常是這樣的
  def add(x:Int)(y:Int)= x+y   // 函數的克里化currying
}

隱式轉換

在面試scala的時候必問的題目。
隱式:偷偷摸摸的進行
目的:悄悄的爲一個類的方法進行增強

目的:把一個Man悄悄的變爲一個SuperMan
悄悄的爲Man這個類增加一個方法,fly,讓Man這個類會fly
舉例:

object ImplicitApp {
  def main(args: Array[String]): Unit = {
    //定義隱式轉換
    //入參是一個man,進去一個man,返回值是一個Superman,出來一個Superman
    implicit def man2superman(man:Man):SuperMan = new SuperMan(man.name)
    val man = new Man("xijinping")
    man.fly()   //man裏面本來是沒有的,隱式轉換後就有fly方法了
  }
}

class Man(val name:String){
  def eat(): Unit ={
    println(s"$name can eat....")
  }
}
class SuperMan(val name:String){
  def fly(): Unit ={
    println(s"$name can fly....")
  }
}

輸出結果:

xijinping can fly....

Process finished with exit code 0

再舉個讀文件增強的例子:

package com.ruozedata.bigdata.scala04
import java.io.File
import scala.io.Source

object ImplicitApp2 {
  def main(args: Array[String]): Unit = {
    implicit def file2RichFile(file:File):RichFile = new RichFile(file)

    val file = new File("E://output.txt")
    val content = file.read()
    println(content)
  }
}
class RichFile(val file:File){
  def read() = Source.fromFile(file.getPath).mkString
}

模式匹配

變量 match {
case 值1 => 代碼
case 值2 => 代碼
case _ =>代碼
}

舉例:

import scala.util.Random

object MatchApp {
  def main(args: Array[String]): Unit = {
    val regions =Array("anhui","shanghai","jiangsu","zhejiang")
    val region = regions(Random.nextInt(regions.length))   //獲取一個隨機數
    println(region)

    region match {
      case "anhui" => println("anhui")
      case "shanghai" => println("shanghai")
      case "jiangsu" => println("jiangsu")
      case _        => println("zhejiang")
    }
  }

上面工作中不怎麼用,用的最多的還是在scala的異常處理裏面。
比如在打開文件,去讀取文件,涉及文件IO讀取的時候,最好加上try catch。
try{
代碼
}
catch{
case //異常處理匹配 這裏用
case
case

}
finally{
比如關閉文件等操作
}
模式既可以匹配值,也可以匹配類型
類型比如說,異常的類型有哪些,看看能匹配到哪個類型。

偏函數 PartitialFunction

工作或面試用的比較多。
模式匹配也可以用偏函數來實現。
先看一下正常的模式匹配怎麼寫:

import scala.util.Random

object MatchApp {
  def main(args: Array[String]): Unit = {
    val regions =Array("anhui","shanghai","jiangsu","zhejiang")
    val region = regions(Random.nextInt(regions.length))
    println(region)
    person(region)

    def person(city:String)= city match {
      case "anhui" => println("anhui person")
      case "shanghai" => println("shanghai person")
      case "jiangsu" => println("jiangsu person")
      case _        => println("zhejiang person")
    }
  }
}

然後看一下偏函數怎麼寫:

import scala.util.Random
object MatchApp {
  def main(args: Array[String]): Unit = {
    val regions =Array("anhui","shanghai","jiangsu","zhejiang")
    val region = regions(Random.nextInt(regions.length))
    println(region)
    println(person2(region))

    def person2:PartialFunction[String,String] = {    //一個偏函數,第一個String可以理解是傳進來的參數,第二個String可以理解是返回值
      case "anhui" => "anhui person"
      case "shanghai" => "shanghai person"
      case "jiangsu" => "jiangsu person"
      case _        => "zhejiang person"
    }
  }
}

偏函數沒有match 。

Tuple

val a = (1,2,3,4,5,6)
括號裏面放了一堆東西,你就可以理解爲Tuple
然後直接可以這樣直接用:
a._1
a._3

舉例:

    val a = ("aaa",111,"bbb")
    val b= a._2     //就代表第二個
    val c= a._3     //就代表第三個
    println(b)
    println(c)

輸出結果:

111
bbb

Process finished with exit code 0
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章