Scala中的惰性val和隐式参数

时间:2012-04-15 13:40:36

标签: scala parameters implicit lazy-evaluation

我一直试图了解隐式参数在Scala中的工作原理。据我所知,隐式参数解析就像这样:

  1. 将对象显式传递给方法。
  2. 范围内定义的隐式定义。
  3. 用作隐式参数的类的伴随对象
  4. 但是,当我开始玩这个懒惰的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的隐式参数,而不使用随播对象中定义的隐式?

2 个答案:

答案 0 :(得分: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根本没有被考虑在内。如果你想要,你必须自己处理初始化。

答案 1 :(得分:1)

尽管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' ...

请注意,如果你有mutable options,你可以只用两行代替懒惰的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

这是非常有意义的:在任何时候你想要访问变量列表的值,你需要一个范围内的Bar,尽管只有第一个计算才有意义。