When to useval or def in Scala traits?

后端 未结 3 731
你的背包
你的背包 2020-11-27 13:03

I was going through the effective scala slides and it mentions on slide 10 to never use val in a trait for abstract members and use def

相关标签:
3条回答
  • 2020-11-27 13:34

    A def can be implemented by either of a def, a val, a lazy val or an object. So it's the most abstract form of defining a member. Since traits are usually abstract interfaces, saying you want a val is saying how the implementation should do. If you ask for a val, an implementing class cannot use a def.

    A val is needed only if you need a stable identifier, e.g. for a path-dependent type. That's something you usually don't need.


    Compare:

    trait Foo { def bar: Int }
    
    object F1 extends Foo { def bar = util.Random.nextInt(33) } // ok
    
    class F2(val bar: Int) extends Foo // ok
    
    object F3 extends Foo {
      lazy val bar = { // ok
        Thread.sleep(5000)  // really heavy number crunching
        42
      }
    }
    

    If you had

    trait Foo { val bar: Int }
    

    you wouldn't be able to define F1 or F3.


    Ok, and to confuse you and answer @om-nom-nom—using abstract vals can cause initialisation problems:

    trait Foo { 
      val bar: Int 
      val schoko = bar + bar
    }
    
    object Fail extends Foo {
      val bar = 33
    }
    
    Fail.schoko  // zero!!
    

    This is an ugly problem which in my personal opinion should go away in future Scala versions by fixing it in the compiler, but yes, currently this is also a reason why one should not use abstract vals.

    Edit (Jan 2016): You are allowed to override an abstract val declaration with a lazy val implementation, so that would also prevent the initialisation failure.

    0 讨论(0)
  • 2020-11-27 13:40

    Always using def seems a bit awkward since something like this won't work:

    trait Entity { def id:Int}
    
    object Table { 
      def create(e:Entity) = {e.id = 1 }  
    }
    

    You will get the following error:

    error: value id_= is not a member of Entity
    
    0 讨论(0)
  • 2020-11-27 13:42

    I prefer not use val in traits because the val declaration has unclear and non-intuitive order of initialization. You may add a trait to already working hierarchy and it would break all things that worked before, see my topic: why using plain val in non-final classes

    You should keep all things about using this val declarations in mind which eventually road you to an error.


    Update with more complicated example

    But there are times when you could not avoid using val. As @0__ had mentioned sometimes you need a stable identifier and def is not one.

    I would provide an example to show what he was talking about:

    trait Holder {
      type Inner
      val init : Inner
    }
    class Access(val holder : Holder) {
      val access : holder.Inner =
        holder.init
    }
    trait Access2 {
      def holder : Holder
      def access : holder.Inner =
        holder.init
    }
    

    This code produces the error:

     StableIdentifier.scala:14: error: stable identifier required, but Access2.this.holder found.
        def access : holder.Inner =
    

    If you take a minute to think you would understand that compiler has a reason to complain. In the Access2.access case it could not derive return type by any means. def holder means that it could be implemented in broad way. It could return different holders for each call and that holders would incorporate different Inner types. But Java virtual machine expects the same type to be returned.

    0 讨论(0)
提交回复
热议问题