【问题标题】:Scala - override a class method in a traitScala - 覆盖特征中的类方法
【发布时间】:2014-11-07 22:49:09
【问题描述】:

我是 Scala 新手(来自 Ruby 世界)。

我对 Scala 中的“特征”概念很好奇(如果我理解正确的话,它应该类似于 ruby​​ 中的模块)。

这是一个用例。

假设我有一个名为User 的类定义如下:

class User {
    def password() : String = "generating a password (default)"
}

假设我有一个特征SecurePasswords,我想使用它“覆盖”User 类中定义的密码方法。

trait SecurePasswords {
    def password() : String = "generating a secure password (non-default)"
}

而且,假设我希望它适用于 User 类的实例,而不是整个类本身。

val a = new User
val b = new User with SecurePasswords

a.password() # generating a password (default)
b.password() # generating a secure password (non-default)

现在这是我期望的理想输出,但是,我得到了不同的错误,例如“anonymous class inherits conflicting members ... (Note: this can be resolved declaring etc etc ...)

这可以在 Scala 中完成还是我要求太多/做了一些非常奇怪的事情? 是否可以不使用任何其他类定义,例如 UserWithSecurePassword extends User

提前谢谢大家!

P.S 如果您想知道“为什么?”,只需假设系统将包含许多需要密码(并且可能还需要安全密码)的实体,因此该特征可以在很多地方使用。

【问题讨论】:

    标签: java scala inheritance overriding traits


    【解决方案1】:

    两个方法定义之间的冲突是因为你没有让它们成为“相同的方法”。你所做的只是让它们巧合地具有相同的名称、参数和返回类型。

    要使它们真正成为相同的方法,以便一个可以覆盖另一个,请在同一个地方定义它们:

    trait HasPassword {
      def password(): String
    }
    
    class User extends HasPassword {
      override def password(): String = "generating a password (default)"
    }
    
    trait SecurePassword extends HasPassword {
      override def password(): String = "generating a password (non-default)"
    }
    
    new User with SecurePassword
    

    通过在 trait HasPassword 中定义,然后在 UserSecurePassword 中重写(而不是 doppleganged 或鸭式),Scala 明白这实际上是重新定义的相同方法。因此,当你混入SecurePassword 时,它可以覆盖User 中的password() 方法。

    【讨论】:

      【解决方案2】:

      除了我之前的回答 - 一种完全不同的方式来获得你想要的东西是将密码函数传递给 User 类,而不是使用特征:

        class User(pw: ()=>String=default) {
          def password = pw
        }
      
        val default = () => "generating a password (default)"
        val secure = () => "generating a secure password (non-default)"
      
        val a = new User()
        val b = new User(secure)
      
        a.password() // generating a password (default)
        b.password() // generating a secure password (non-default)
      

      如图所示,您可以使用默认参数来避免在默认情况下必须指定密码函数。

      【讨论】:

      • 仔细观察后,我认为这个解决方案比使用特征的解决方案更简单,更优雅。
      【解决方案3】:

      我不确定这个用例是什么。为什么不在它的类定义中只包含用户(用 Ruby 术语)“mixin”、SecurePasswords 特征和覆盖密码?

      来自 Ruby,这可能更难获得,但 Scala 是一种编译语言,像这样动态/即时更改类定义通常不是一个好主意。将 Scala 中的类型系统视为测试代码的一种方式。您将代码解释推迟到运行时越多,代码的可测试性/安全性就越低。这是 Scala/Java/Haskell/...(插入编译类型语言)的优势之一——类型系统可以在编译时捕获很多错误。利用这个优势,不要与之抗争。

      我会研究隐式参数的使用以及它们如何与特征关联/使用。

      此外,如果没有更广泛的背景,即您试图在代码中使用此模式完成什么,很难知道,但如果您尝试实现某种适配器模式,此链接可能对您有用。

      http://danielwestheide.com/blog/2013/02/06/the-neophytes-guide-to-scala-part-12-type-classes.html

      【讨论】:

        【解决方案4】:

        您可以为每个实例提供不同的密码行为,如下所示 - 但您需要在每种情况下(默认或安全)提供明确的特征:

          abstract class User {
            def password(): String
          }
        
          trait SecurePasswords {
            def password(): String = "generating a secure password (non-default)"
          }
        
          trait DefaultPasswords {
            def password(): String = "generating a password (default)"
          }
        
          val a = new User with DefaultPasswords
          val b = new User with SecurePasswords
        
          a.password() // generating a password (default)
          b.password() // generating a secure password (non-default)
        

        更新:但是,我认为 Dan Getz 的回答可能更接近您最初的要求

        【讨论】:

          【解决方案5】:

          注意:关于错误消息,有一个待处理的issue 128“从java默认方法继承冲突成员时没有歧义错误”

          它应该在 scala 2.12.x 中由 commit 3a3688f64 解决

          SD-128 修复对 default 方法的覆盖检查

          默认检查继承两个冲突成员是错误的 方法,导致丢失错误消息。

          我们在覆盖一个 默认方法。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-07-01
            • 2011-01-03
            • 1970-01-01
            • 2017-01-25
            • 1970-01-01
            • 2015-10-31
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多