Scala的高級特性

 

 高階函數

概念

Scala混合了面向對象和函數式的特性,咱們一般將能夠做爲參數傳遞到方法中的表達式叫作函數。在函數式編程語言中,函數是「頭等公民」,高階函數包含:做爲值的函數、匿名函數、閉包、柯里化等等。java

做爲值的函數   

能夠像任何其餘數據類型同樣被傳遞和操做的函數,每當你想要給算法傳入具體動做時這個特性就會變得很是有用。算法

 

 

定義函數時格式:val 變量名 (輸入參數類型和個數)  =>  函數實現和返回值類型編程

=」表示將函數賦給一個變量閉包

=>」左面表示輸入參數名稱、類型和個數,右邊表示函數的實現和返回值類型編程語言

匿名函數

在Scala中,你不須要給每個函數命名,沒有將函數賦給變量的函數叫作匿名函數。函數式編程

 

 

因爲Scala能夠自動推斷出參數的類型,全部能夠寫的跟精簡一些函數

 

 

還記得神奇的下劃線嗎?這纔是終極方式spa

 

 

柯里化

什麼是柯里化

柯里化(Currying)指的是把原來接受多個參數的函數變換成接受一個參數的函數過程,而且返回接受餘下的參數且返回結果爲一個新函數的技術。scala

 

 

 

例子

(1)     一個普通的非柯里化的函數定義,實現一個加法函數:3d

 

scala> def plainOldSum(x:Int,y:Int)=x+y
plainOldSum: (x: Int, y: Int)Int
 
scala> plainOldSum(1,2)
res0: Int = 3

 


(2)     使用「柯里化」技術來定義這個加法函數,原來函數使用一個參數列表,「柯里化」,把函數定義爲多個參數列表: 

scala> def curriedSum(x:Int)(y:Int)=x+y
curriedSum: (x: Int)(y: Int)Int
 
scala> curriedSum(1)(2)
res1: Int = 3
 
當你調用curriedSum (1)(2)時,其實是依次調用兩個普通函數(非柯里化函數),
第一次調用使用一個參數x,返回一個函數類型的值,
第二次使用參數y調用這個函數類型的值。

 


(3)     使用下面兩個分開的定義在模擬curriedSum柯里化函數: 

首先定義第一個函數:
scala> def first(x:Int)=(y:Int)=>x+y
first: (x: Int)Int => Int
 
而後咱們使用參數1調用這個函數來生成第二個函數:
scala> val second =first(1)
second: Int => Int = <function1>
scala> second(2)
res2: Int = 3

 


(4)     使用curriedSum 來定義second 

 

scala> val onePlus=curriedSum(1)_
onePlus: Int => Int = <function1>

 
下劃線「_」 做爲第二參數列表的佔位符, 這個定義的返回值爲一個函數,當調用時會給調用的參數加一。
 
scala> onePlus(2)
res3: Int = 3

調用生成的函數,給函數傳入參數,便可獲得咱們想要的結果。

 

  

總結

scala柯里化風格的使用能夠簡化主函數的複雜度,提升主函數的自閉性,提升功能上的可擴張性、靈活性。能夠編寫出更加抽象,功能化和高效的函數式代碼。

閉包

什麼是閉包

閉包是一個函數,返回值依賴於聲明在函數外部的一個或多個變量。
閉包一般來說能夠簡單的認爲是能夠訪問不在當前做用域範圍內的一個函數。

例子

package cn.itcast.closure

/**
  * scala中的閉包
  * 閉包是一個函數,返回值依賴於聲明在函數外部的一個或多個變量。
  */
object ClosureDemo {

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

      //變量y不處於其有效做用域時,函數還可以對變量進行訪問
        val add=(x:Int)=>{
          x+y
        }

    //在add中有兩個變量:x和y。其中的一個x是函數的形式參數,
    //在add方法被調用時,x被賦予一個新的值。
    // 然而,y不是形式參數,而是自由變量
    println(add(5)) // 結果15
  }
}

 

 

隱式轉換和隱式參數

隱式轉換

Scala提供的隱式轉換和隱式參數功能,是很是有特點的功能。是Java等編程語言所沒有的功能。它能夠容許你手動指定,將某種類型的對象轉換成其餘類型的對象或者是給一個類增長方法。經過這些功能,能夠實現很是強大、特殊的功能。

Scala的隱式轉換,其實最核心的就是定義隱式轉換方法,即implicit conversion function。定義的隱式轉換方法,只要在編寫的程序內引入,就會被Scala自動使用。Scala會根據隱式轉換方法的簽名,在程序中使用到隱式轉換方法接收的參數類型定義的對象時,會自動將其傳入隱式轉換方法,轉換爲另一種類型的對象並返回。這就是「隱式轉換」。其中全部的隱式值和隱式方法必須放到object中

然而使用Scala的隱式轉換是有必定的限制的,總結以下:

  • implicit關鍵字只能用來修飾方法、變量(參數)。
  • 隱式轉換的方法在當前範圍內纔有效。若是隱式轉換不在當前範圍內定義(好比定義在另外一個類中或包含在某個對象中),那麼必須經過import語句將其導。

隱式參數

所謂的隱式參數,指的是在函數或者方法中,定義一個用implicit修飾的參數,此時Scala會嘗試找到一個指定類型的,用implicit修飾的參數,即隱式值,並注入參數。

Scala會在兩個範圍內查找:

  • 當前做用域內可見的val或var定義的隱式變量;
  • 一種是隱式參數類型的伴生對象內的隱式值;

 

 

隱式轉換方法做用域與導入

(1)Scala默認會使用兩種隱式轉換,一種是源類型或者目標類型的伴生對象內的隱式轉換方法;一種是當前程序做用域內的能夠用惟一標識符表示的隱式轉換方法。

(2)若是隱式轉換方法不在上述兩種狀況下的話,那麼就必須手動使用import語法引入某個包下的隱式轉換方法,好比import test._。一般建議,僅僅在須要進行隱式轉換的地方,用import導入隱式轉換方法,這樣能夠縮小隱式轉換方法的做用域,避免不須要的隱式轉換。

隱式轉換的時機

(1)當對象調用類中不存在的方法或成員時,編譯器會自動將對象進行隱式轉換

(2)當方法中的參數的類型與目標類型不一致時

 

隱式轉換和隱式參數案例

隱式轉換案例一

(讓File類具有RichFile類中的read方法)

package cn.itcast.implic_demo

import java.io.File
import scala.io.Source

object MyPredef{
  //定義隱式轉換方法
  implicit def file2RichFile(file: File)=new RichFile(file)
}

class RichFile(val f:File) {
  def read()=Source.fromFile(f).mkString
}

object RichFile{
  def main(args: Array[String]) {
    val f=new File("E://words.txt")

    //使用import導入隱式轉換方法
    import MyPredef._
    //經過隱式轉換,讓File類具有了RichFile類中的方法
    val content=f.read()
    println(content)
  }
}

 

 

隱式轉換案例二

(超人變身)

package cn.itcast.implic_demo

class Man(val name:String)
class SuperMan(val name: String) {
  def heat=print("超人打怪獸")
}

object SuperMan{
  //隱式轉換方法
  implicit def man2SuperMan(man:Man)=new SuperMan(man.name)
  def main(args: Array[String]) {
      val hero=new Man("hero")

      //Man具有了SuperMan的方法
      hero.heat
  }
}

 

隱式轉換案例三

(一個類隱式轉換成具備相同方法的多個類)

package cn.itcast.implic_demo

class A(c:C) {
    def readBook(): Unit ={
      println("A說:好書好書...")
    }
}
class B(c:C){
  def readBook(): Unit ={
    println("B說:看不懂...")
  }
  def writeBook(): Unit ={
    println("B說:不會寫...")
  }
}
class C
object AB{
  //建立一個類的2個類的隱式轉換
  implicit def C2A(c:C)=new A(c)
  implicit def C2B(c:C)=new B(c)
}

object B{
  def main(args: Array[String]) {
    //導包
    //1. import AB._ 會將AB類下的全部隱式轉換導進來
    //2. import AB._C2A 只導入C類到A類的的隱式轉換方法
    //3. import AB._C2B 只導入C類到B類的的隱式轉換方法
    import AB._
    val c=new C
    //因爲A類與B類中都有readBook(),只能導入其中一個,不然調用共同方法時代碼報錯
    //c.readBook()
    //C類能夠執行B類中的writeBook()
    c.writeBook()
  }
}

 



隱式參數案例四

(員工領取薪水)

package cn.itcast.implic_demo

object Company{
  //在object中定義隱式值    注意:同一類型的隱式值只容許出現一次,不然會報錯
  implicit  val aaa="zhangsan"
  implicit  val bbb=10000.00
}

class Boss {
  //注意參數匹配的類型   它須要的是String類型的隱式值
  def callName()(implicit name:String):String={
    name+" is coming !"
  }

  //定義一個用implicit修飾的參數
  //注意參數匹配的類型    它須要的是Double類型的隱式值
  def getMoney()(implicit money:Double):String={
   " 當月薪水:"+money
  }
}

object Boss extends App{
  //使用import導入定義好的隱式值,注意:必須先加載不然會報錯
  import Company._
  val boss =new Boss
  println(boss.callName()+boss.getMoney())
}
相關文章
相關標籤/搜索