[英]Lazy vals and implicit parameters in Scala
我一直試圖了解隱式參數在Scala中的工作原理。 據我所知,隱式參數解析如下:
然而,當我開始玩這個懶惰的vals時,我得到了一些幫助。 懶惰的val似乎只使用最后的解析規則。 以下是一些示例代碼:
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中,它在訪問列表時也錯過了本地定義的隱式Bar。
是否有任何方法可以使用隱式val的隱式參數,而不使用隨播對象中定義的隱式?
這是因為當編譯器編譯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根本沒有被考慮在內。 如果你想要,你必須自己處理初始化。
雖然scala不支持帶隱式參數的延遲val,但您可以使用選項自行定義。 因此,解決方案是替換:
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' ...
請注意,如果您有可變選項 ,則可以僅使用兩行替換延遲val以獲得相同的結果。
private val _list: MutableOpt[List[Int]] = MutableOpt.from(None)
def list(implicit f: Bar) = _list.getOrSet(initialize)
就個人而言,我希望有一天Scala會讓我們用一行來寫這個:
lazy val list(implicit f: Bar) = initialize
這將是非常有意義的:在任何時候你想要訪問變量列表的值,你需要一個范圍內的條,雖然只有第一個計算是重要的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.