溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Scala 方法和函數(shù)的區(qū)別是什么

發(fā)布時(shí)間:2021-07-27 18:25:43 來源:億速云 閱讀:177 作者:Leah 欄目:大數(shù)據(jù)

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)碛嘘P(guān)Scala 方法和函數(shù)的區(qū)別是什么,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

Scala中既有函數(shù)(Function)也有方法(Method),大多數(shù)情況下我們都可以不去理會(huì)他們之間的區(qū)別。但是有時(shí)候我們必須要了解他們之間的不同。

Scala 中的方法跟 Java 的方法一樣,方法是組成類的一部分。方法有名字、類型簽名,有時(shí)方法上還有注解,以及方法的功能實(shí)現(xiàn)代碼(字節(jié)碼)。

Scala 中的函數(shù)是一個(gè)完整的對(duì)象。Scala 中用 22 個(gè)特質(zhì)(trait)抽象出了函數(shù)的概念。這 22 特質(zhì)從 Function1 到 Function22:

Scala 方法和函數(shù)的區(qū)別是什么

如上圖中的 Function10 代表的是:有 10 個(gè)形參,返回值為 R(協(xié)變)的函數(shù)。

函數(shù)更常使用的是匿名函數(shù),定義的時(shí)候只需要說明輸入?yún)?shù)的類型和函數(shù)體即可,不需要名稱如果你要是用的話,一般會(huì)把這個(gè)匿名函數(shù)賦值給一個(gè)變量(其實(shí)是val常量) 
 表現(xiàn)形式:(傳入?yún)?shù))=>{方法體}

val f = (name:String)=>println("Hi,"+name)
 f("kafka")

Scala 中的函數(shù)其實(shí)就是繼承了這些 Trait 的類的對(duì)象,如:我們通過函數(shù)字面量定義一個(gè)函數(shù)

Scala 方法和函數(shù)的區(qū)別是什么

 其實(shí)上述函數(shù)的定義方式跟如下定義方式等同:

Scala 方法和函數(shù)的區(qū)別是什么

由于 Function2 是特質(zhì),不能直接 new。上述 new Function2[Int,Int,Int](){} 其實(shí)是定義并實(shí)例化一個(gè)實(shí)現(xiàn)了 Function2 特質(zhì)的類的對(duì)象。

apply 是 scala 中的語法糖:對(duì)一個(gè)對(duì)象 obj 上調(diào)用 obj(),scala 編譯器會(huì)轉(zhuǎn)換為 obj.apply();在一個(gè)類 clazz 上調(diào) clazz(),scala 編譯器會(huì)轉(zhuǎn)換為 clazz_company_obj.apply(),其中 clazz_company_obj 為 clazz 的伴生對(duì)象。

具體的差異,總結(jié)為如下幾點(diǎn):

1. 方法不能作為單獨(dú)的表達(dá)式而存在(參數(shù)為空的方法除外),而函數(shù)可以。如:

scala> def m(x:Int) = 2.0*x
m: (x: Int)Double   方法的定義

scala> val f = (x:Int)=> 2.0*x 
f: Int => Double = <function1>  函數(shù)定義

scala> f
res7: Int => Double = <function1> 函數(shù)就是

scala> val tmp = m _  通過這個(gè)方式還可以實(shí)現(xiàn)方法到函數(shù)的變化
tmp: Int => Double = <function1>

scala> m  直接調(diào)用方法名是錯(cuò)誤的
<console>:13: error: missing argument list for method m
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `m _` or `m(_)` instead of `m`.
       m
       ^

在Scala語言中, 函數(shù)也是對(duì)象, 每一個(gè)對(duì)象都是scala.FunctionN(1-22)的實(shí)例, 其中N是函數(shù)參數(shù)的數(shù)量, 例如我們定義一個(gè)函數(shù)并復(fù)制給一個(gè)變量:

scala> val f = (x: Int) => x + 1 (匿名函數(shù)的寫法)
f: Int => Int = <function1>
 這里定義了一個(gè)接收一個(gè)整型變量作為參數(shù)的函數(shù), 函數(shù)的功能是返回輸入?yún)?shù)加1. 可以看到REPL返回參數(shù)的toString方法 即 <function0> . 
 那么如果我們有一個(gè)指向函數(shù)對(duì)象的引用, 我們?cè)撊绾握{(diào)用這個(gè)函數(shù)呢? 答案是通過FunctionN的 apply 方法, 即 FunctionN.apply() , 因此調(diào)用函數(shù)對(duì)象的方法如下: 

scala> f.apply(3)
res2: Int = 4
但是如果每次調(diào)用方法對(duì)象都要通過FunctionN.apply(x, y...), 就會(huì)略顯啰嗦, Scala提供一種模仿函數(shù)調(diào)用的格式來調(diào)用函數(shù)對(duì)象

scala> f(3)
res3: Int = 4

在如上的例子中,我們首先定義了一個(gè)方法 m,接著有定義了一個(gè)函數(shù)f。接著我們把函數(shù)名(函數(shù)值)當(dāng)作最終表達(dá)式來用,由于f本身就是一個(gè)對(duì)象(實(shí)現(xiàn)了 FunctionN 特質(zhì)的對(duì)象),所以這種使用方式是完全正確的。但是我們把方法名當(dāng)成最終表達(dá)式來使用的話,就會(huì)出錯(cuò)。

2.方法可以沒有參數(shù)列表而 函數(shù)必須要有參數(shù)列表

scala> def m1 = 100
m1: Int 沒有入?yún)⒌姆椒ǘx 是下面的簡(jiǎn)寫形式

scala> def m2() = 100
m2: ()Int 無入?yún)⒌姆诺蕉x

scala> val f1 = ()=>100  無入?yún)⒌暮瘮?shù)定義
f1: () => Int = <function0>

scala> val f1 = => 100  仿照最上面寫就直接報(bào)錯(cuò)
<console>:1: error: illegal start of simple expression
val f1 = => 100

在如上的例子中,m1方法接受零個(gè)參數(shù),所以可以省略參數(shù)列表。而函數(shù)不能省略參數(shù)列表。

3.方法名是方法調(diào)用,而函數(shù)名只是代表函數(shù)對(duì)象本身

這個(gè)比較容易理解。因?yàn)楸4婧瘮?shù)字面量的變量(又稱為函數(shù)名或者函數(shù)值)本身就是實(shí)現(xiàn)了 FunctionN 特質(zhì)的類的對(duì)象,要調(diào)用對(duì)象的 apply方法,就需要使用obj()的語法。所以函數(shù)名后面加括號(hào)才是調(diào)用函數(shù)。如下:

scala> def m1 = 100
m1: Int 方法定義

scala> val f1 = ()=> 100 函數(shù)定義
f1: () => Int = <function0>

scala> m1 方法調(diào)用
res11: Int = 100

scala> f1 函數(shù)查看
res12: () => Int = <function0>

scala> f1() 函數(shù)調(diào)用,是下面調(diào)用方式的簡(jiǎn)單版
res13: Int = 100

scala> f1.apply() 函數(shù)調(diào)用的正確形式
res14: Int = 100

4.在需要函數(shù)的地方,如果傳遞一個(gè)方法,會(huì)自動(dòng)進(jìn)行ETA展開(把方法轉(zhuǎn)換為函數(shù))

Scala 方法和函數(shù)的區(qū)別是什么

如上,如果我們直接把一個(gè)方法賦值給變量會(huì)報(bào)錯(cuò)。如果我們指定變量的類型就是函數(shù),那么就可以通過編譯,如下:

scala> val f1:(Int)=>Int = m
f1: Int => Int = <function1>

當(dāng)然我們也可以強(qiáng)制把一個(gè)方法轉(zhuǎn)換給函數(shù),這就用到了 scala 中的部分應(yīng)用函數(shù):

scala> val f1 = m _ 
f1: Int => Int = <function1>

scala> val f1 = m(_)
f1: Int => Int = <function1>

5.傳名參數(shù)本質(zhì)上是個(gè)方法

傳名參數(shù)實(shí)質(zhì)上是一個(gè)參數(shù)列表為空的方法,因?yàn)楹瘮?shù)的話參數(shù)列表是不能為空的!(區(qū)別2參考),如下:

scala> def m1(x: =>Int) = List(x,x)
m1: (x: => Int)List[Int]

如上代碼實(shí)際上定義了一個(gè)方法 m1,m1 的參數(shù)是個(gè)傳名參數(shù)(方法)。由于對(duì)于參數(shù)為空的方法來說,方法名就是方法調(diào)用 ,所以List(x,x)實(shí)際上是進(jìn)行了兩次方法調(diào)用。

Scala 方法和函數(shù)的區(qū)別是什么

由于 List(x,x) 是進(jìn)行了兩次方法調(diào)用,所以得到兩個(gè)不同的值。如果我們稍微修改一下函數(shù)的m1的定義,把x先緩存起來,結(jié)果就會(huì)跟以前大不一樣。

scala> def m1(x: => Int) = {val y = x;List(y,y)}
m1: (x: => Int)List[Int]

scala> m1(r.nextInt)
res18: List[Int] = List(-723271792, -723271792)

6. 方法跟函數(shù)當(dāng)參數(shù)的調(diào)用

    // 方法定義
    def method1(arge1: Int, arge2: Int) = arge1 + arge2

    // 函數(shù)定義
    val funct1 = (arge1: Int, arge2: Int) => arge1 - arge2

    def method2(f: (Int, Int) => Int) = f(12, 12)

    println("方法傳方法" + method2(method1))
    
    val funct2 = (f: (Int, Int) => Int) => f(22, 22)
    println("函數(shù)傳方法" + funct2(method1))
    
    def method3(f: (Int, Int) => Int) = f(4, 1)
    println("方法傳函數(shù)" + method3(funct1))

    val funct3 = (f: (Int, Int) => Int) => f(5, 1)
    println("函數(shù)傳函數(shù)" + funct3(funct1))

--------------------------------------------------------------------------------------

    //  調(diào)用方式一 方法調(diào)用方法
    def method1(): Unit = println("printmethod1")

    def method2(m: () => Unit): Unit = m() // 如果參數(shù)列表寫的是帶()  調(diào)用的時(shí)候也要帶()


    method2(method1) //執(zhí)行

    // 調(diào)用方式二 方法調(diào)用方法
    def method11: Unit = println("printmethod11")

    def method22(m: => Unit) = m // 如果參數(shù)列表中 就沒有() 此處不可寫m()

    method22(method11) //執(zhí)行

    //調(diào)用方式三
    def method111(): Unit = println("printmethod111")

    def method222(m: () => Unit) = m // 如果參數(shù)列表帶()  調(diào)用時(shí)候不帶(),則不會(huì)執(zhí)行

    method222(method111) //此處沒有輸出

Scala 中Apply講解

class ApplyTest{
  def apply() = println("I am into Spark so much!!!")
   
  def haveATry{
    println("Have a try on apply!")
  }
}
object ApplyTest{
  def apply() = {
    println("I am into Scala so much!!!")
    new ApplyTest
  }
}
object ApplyOperation {
  def main(args: Array[String]) {
    val array = Array(1,2,3,4,5)
    val a = ApplyTest() //這里并沒有new,然后確實(shí)返回了類的實(shí)例
    a.haveATry 
  }
}

輸出結(jié)果:

I am into Scala so much!!!
Have a try on apply!

在一個(gè)類的伴生對(duì)象里面,實(shí)現(xiàn)apply方法,在這里面可以創(chuàng)建類的實(shí)例。譬如val a = Array(1, 2, 3)就是使用了Array的apply方法。

同樣,在class里面也可以使用apply方法:

object ApplyOperation {
  def main(args: Array[String]) {
     val a = new ApplyTest
     a.haveATry
     println(a())  //調(diào)用class的apply方法
  }
}

結(jié)果:

Have a try on apply!
I am into Spark so much!!!
()

上述就是小編為大家分享的Scala 方法和函數(shù)的區(qū)別是什么了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI