2012-04-15 67 views
5

我一直試圖弄清楚隱式參數如何在Scala中工作。據我可以告訴隱式參數分辨率是這樣的:Scala中的惰性vals和隱式參數

  1. 顯式傳遞一個對象的方法。
  2. 在範圍中定義的隱式定義。作爲一個隱含參數

然而類的

  • 伴侶的對象,當我開始結合瓦爾斯懶此玩弄我有點supprise的。似乎懶惰的vals只使用最後的解析規則。下面是一些示例代碼來說明:

    class Bar(val name:String) 
    object Bar { implicit def bar = new Bar("some default bar") } 
    
    class Foo { 
        lazy val list = initialize 
        def initialize(implicit f:Bar) = { 
        println("initialize called with Bar: '" + f.name + "' ...") 
        List[Int]() 
        } 
    } 
    
    trait NonDefaultBar extends Foo { 
        implicit def f = new Bar("mixed in implicit bar") 
        def mixedInInit = initialize 
        lazy val mixedInList = list 
    } 
    
    object Test { 
        def test = { 
         println("Case 1: with implicitp parameter from companion object") 
         val foo1 = new Foo 
         foo1.list 
         foo1.initialize 
    
         println("Case 2: with mixedin implicit parameter overriding the default one...") 
         val foo2 = new Foo with NonDefaultBar 
         foo2.mixedInList 
    
         val foo3 = new Foo with NonDefaultBar 
         foo3.mixedInInit 
    
         println("Case 3: with local implicit parameter overriding the default one...") 
         implicit def nonDefaultBar = new Bar("locally scoped implicit bar") 
         val foo4 = new Foo 
         foo4.list 
         foo4.initialize 
        } 
    } 
    

    調用Test.test給出了下面的輸出:

    Case 1: with implicitp parameter from companion object 
    initialize called with Bar: 'some default bar' ... 
    initialize called with Bar: 'some default bar' ... 
    Case 2: with mixedin implicit parameter overriding the default one... 
    initialize called with Bar: 'some default bar' ... 
    initialize called with Bar: 'mixed in implicit bar'... 
    Case 3: with local implicit parameter overriding the default one... 
    initialize called with Bar: 'some default bar' ... 
    initialize called with Bar: 'locally scoped implicit bar' ... 
    

    爲什麼編譯器抓不住,有案例2調用mixedInList當混合隱吧。在情況3中,它在訪問列表時也忽略了本地定義的隱式條。

    是否有任何方法使用隱式參數與惰性vals不使用隱式定義的伴隨對象?

  • 回答

    4

    這是因爲在編譯器編譯Foo類時沒有其他implicit Bar。在Java反編譯的代碼如下所示:

    public class Foo 
        implements ScalaObject 
    { 
        private List<Object> list; 
        public volatile int bitmap$0; 
    
        public List<Object> list() 
        { 
        if (
         (this.bitmap$0 & 0x1) == 0); 
        synchronized (this) 
        { 
         if (
         (this.bitmap$0 & 0x1) == 0) { 
         this.list = initialize(Bar..MODULE$.bar()); this.bitmap$0 |= 1; } return this.list; 
        } 
        } 
        public List<Object> initialize(Bar f) { Predef..MODULE$.println(new StringBuilder().append("initialize called with Bar: '").append(f.name()).append("' ...").toString()); 
        return Nil..MODULE$; 
        } 
    } 
    

    lazy val僅僅是檢查變量是否已經設置的方法,要麼返回它,或設置它,然後返回它。所以你的mixin根本沒有被考慮到。如果你想這樣做,你必須自己照顧初始化。

    +0

    好吧,但*不應該*它承認implicits?它們在編譯時是已知的嗎?也許它會工作,如果我將隱式參數移動到構造函數? – 2012-04-15 14:36:37

    +0

    它識別編譯時在範圍內唯一隱含的Bar,這是Bar伴侶中的一個。因爲'list'只在'Foo'上定義,所以它會一直使用這個。您可以在您的構造函數中定義隱式,但特徵中的隱式仍然不在作用域中,因爲它只存在於實例上。 – drexin 2012-04-15 14:59:10

    +0

    因此,如果我將惰性val和初始化方法移動到NonDefaultBar特性並讓該特性擴展Foo並將隱式def移動到Foo,那麼編譯器會知道在Foo中獲取隱式def '評估'懶惰的列表'? – 2012-04-15 16:00:41

    0

    雖然scala不支持帶隱式參數的惰性vals,但您可以使用選項自定義它。因此,解決辦法是更換:

    lazy val list = initialize 
    

    通過

    private var _list: Option[List[Int]] = None 
    def list(implicit f: Bar) = 
        _list.getOrElse{ 
        _list = Some(initialize) 
        _list.get 
        } 
    

    然後運行Test.test顯示預期的結果:

    Case 1: with implicitp parameter from companion object 
    initialize called with Bar: 'some default bar' ... 
    initialize called with Bar: 'some default bar' ... 
    Case 2: with mixedin implicit parameter overriding the default one... 
    initialize called with Bar: 'mixed in implicit bar' ... 
    initialize called with Bar: 'mixed in implicit bar' ... 
    Case 3: with local implicit parameter overriding the default one... 
    initialize called with Bar: 'locally scoped implicit bar' ... 
    initialize called with Bar: 'locally scoped implicit bar' ... 
    

    請注意,如果你有mutable options,你可以取代你的懶惰val只有兩行來獲得相同的結果。

    private val _list: MutableOpt[List[Int]] = MutableOpt.from(None) 
    def list(implicit f: Bar) = _list.getOrSet(initialize) 
    

    就個人而言,我希望有一天斯卡拉將讓我們寫這使用一個行:

    lazy val list(implicit f: Bar) = initialize 
    

    這將是非常有意義的:在您要訪問的變量列表的價值的時候,你需要一個範圍的Bar,雖然只有第一個計算會很重要。