在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Scala/ 延遲初始化(Lazy vals)
 初始化抽象 vals
延遲初始化(Lazy vals)
Type 成員
抽象類型
預(yù)先初始化成員的值

延遲初始化(Lazy vals)

除了前面介紹的預(yù)先初始化成員值外,你還是讓系統(tǒng)自行決定何時(shí)初始化成員的初始值,這是通過(guò)在 val 定義前面添加 lazy(懶惰),也是說(shuō)直到你第一次需要引用該成員是,系統(tǒng)才會(huì)去初始化,否則該成員就不初始化(這也是 lazy 的由來(lái):-)). 首先我們定義一個(gè)正常定義 val 的例子:


object Demo {
    val x = { println("initializing x"); "done"}
}

我們首先引用 Demo,然后 Demo.x


scala> Demo
initializing x
res0: Demo.type = Demo$@78178c35

scala> Demo.x
res1: String = done

正如你所看到的,當(dāng)引用 Demo 對(duì)象時(shí),它的成員 x 也會(huì)初始化,初始化 x 伴隨著初始化 Demo 的過(guò)程。然后,如果我們?cè)?val x 前添加 lazy ,情況就有所不同了:


object Demo {
    lazy val x = { println("initializing x"); "done"}
}

defined object Demo

scala> Demo
res0: Demo.type = Demo$@7de1c412

scala> Demo.x
initializing x
res1: String = done

在使用 lazy 之后,初始化 Demo 時(shí),不會(huì)初始化 x,只有在引用到 Demo.x 該初始化代碼才會(huì)執(zhí)行。 這有點(diǎn)類似定義了一個(gè)無(wú)參數(shù)的方法,但和 def 不同的是,lazy 變量初始化代碼只會(huì)執(zhí)行一次。 通過(guò)這個(gè)例子,我們可以看到例如 Demo 的對(duì)象本身也像一個(gè) lazy 變量,也是在第一次引用時(shí)才會(huì)初始化,這是正確的,實(shí)際上一個(gè) object 定義可以看成是使用了lazy val定義一個(gè)匿名類實(shí)例的簡(jiǎn)化方式。

使用l azy val,我們可以修改之前的 RationalTrait, 在這個(gè)新的 Trait 定義中,所有的類成員變量的實(shí)現(xiàn)(非抽象成員)都使用 lazy 來(lái)修飾。


trait LazyRationalTrait{
    val numerArg :Int
    val denomArg :Int

    lazy val numer = numerArg/g
    lazy val denom = denomArg/g

    private lazy val g = {
        require(denomArg !=0)
        gcd(numerArg,denomArg)
    }
    private def gcd(a:Int,b:Int):Int =
        if(b==0) a else gcd(b, a % b)

    override def toString = numer + "/" + denom
}

同時(shí)我們把 require 移動(dòng)到 g 里面,這樣所有的 lazy val 初始化代碼都移動(dòng)到 val 定義的右邊。我們不再需要預(yù)先初始化成員變量。測(cè)試如下:


scala> val x = 2
x: Int = 2

scala> new LazyRationalTrait{
    val numerArg = x
    val denomArg = 2 * x
}

res2: LazyRationalTrait = 1/2

我們來(lái)分析一下這段代碼中命令行的執(zhí)行順序:

  1. 首先,創(chuàng)建了一個(gè)新的 LazyRationalTrait 的實(shí)例,執(zhí)行 LazyRationalTrait 的初始化代碼,這部分代碼為空,LazyRationalTrait 所有成員變量都沒有初始化。
  2. 其次,該 Trait 的匿名子類的主構(gòu)造函數(shù)被執(zhí)行,這部分初始化 numberArg 和 denomArg 為2和4.
  3. 接下來(lái),命令行需要調(diào)用該實(shí)例的 toString 方法來(lái)顯示該實(shí)例的值。
  4. 接下來(lái),toString 需要訪問成員 number 這是第一次訪問該成員,因此 lazy val 初始化代碼被執(zhí)行。初始化代碼調(diào)用私有成員g,因此需要計(jì)算 g 的值,用到之前定義過(guò)的 numberArg 和 denomArg。
  5. 接下來(lái) toString 需要訪問成員 denom 這是第一次訪問該成員,因此 lazy val 初始化代碼被執(zhí)行。初始化代碼調(diào)用私有成員 g ,因此需要計(jì)算 g 的值,因?yàn)?g 已經(jīng)計(jì)算過(guò),無(wú)需再計(jì)算。
  6. 最后,toString 的結(jié)果1/2構(gòu)造出來(lái)并顯示。

在這個(gè)例子中,我們?cè)趯懘a時(shí),g 定義在 number 和 denom 的后面,然而,由于這三個(gè)變量都是使用 lazy 來(lái)定義的,因此它們?cè)诖a中出現(xiàn)的順序并不重要。

上一篇:Type 成員下一篇: 初始化抽象 vals