2012-04-15 44 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,虽然只有第一个计算会很重要。