首页 文章

何时在Scala特征中使用val或def?

提问于
浏览
73

我正在浏览effective scala slides并且它在幻灯片10中提到永远不要在 trait 中使用 val 来抽象成员并使用 def 代替 . 幻灯片没有详细提及为什么在 trait 中使用抽象 val 是反模式 . 如果有人可以解释在抽象方法的特性中使用val vs def的最佳实践,我将不胜感激

3 回答

  • 7

    def 可以由 defvallazy valobject 实现 . 所以这是定义成员最抽象的形式 . 因为traits通常是抽象接口,所以你想要 val 说明实现应该如何做 . 如果你要求 val ,实现类不能使用 def .

    仅当您需要稳定的标识符时才需要 val ,例如对于路径依赖类型 . 那是's something you usually don't需要 .


    相比:

    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
      }
    }
    

    如果你有

    trait Foo { val bar: Int }
    

    你将无法定义 F1F3 .


    好的,混淆你并回答@ om-nom-nom-using abstract val s会导致初始化问题:

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

    这是一个丑陋的问题,在我个人看来,它应该在未来的Scala版本中通过修复它在编译器中消失,但是,是的,目前这也是为什么不应该使用abstract val s的原因 .

    Edit (2016年1月):您可以使用 lazy val 实现覆盖抽象 val 声明,这样也可以防止初始化失败 .

  • 108

    我不喜欢在特征中使用 val 因为val声明具有不清楚且不直观的初始化顺序 . 你可以在已经工作的层次结构中添加一个特征,它会破坏之前工作的所有东西,请参阅我的主题:why using plain val in non-final classes

    你应该记住使用这个val声明的所有事情,这最终会导致你的错误 .


    使用更复杂的示例进行更新

    但有些时候你无法避免使用 val . 正如@ 0__有时提到你需要一个稳定的标识符而 def 不是一个 .

    我举一个例子来说明他在说什么:

    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
    }
    

    此代码生成错误:

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

    如果你花一点时间认为你会理解编译器有理由抱怨 . 在 Access2.access 情况下,它无法通过任何方式获得返回类型 . def holder 意味着它可以广泛实施 . 它可以为每次调用返回不同的持有者,并且持有者将包含不同的 Inner 类型 . 但Java虚拟机需要返回相同的类型 .

  • -4

    总是使用def看起来有点尴尬,因为这样的东西不起作用:

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

    您将收到以下错误:

    error: value id_= is not a member of Entity
    

相关问题