首页 文章

Scala - 覆盖特征中的类方法

提问于
浏览
6

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

而且我对Scala中的“traits”概念感到好奇(如果我理解正确的话,它应该〜类似于ruby中的模块) .

这是一个用例 .

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

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

并假设我有一个特性 SecurePasswords 使用我想"override"在 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

谢谢大家!

如果你想知道“为什么?”,只要假设系统包含许多需要密码的实体(可能还有安全密码),那么这个特性可以在很多地方使用 .

5 回答

  • 11

    两种方法定义之间的冲突是因为您没有使它们成为“相同的方法” . 你所做的就是让它们巧合地拥有相同的名称,参数和返回类型 .

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

    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
    

    通过在特征 HasPassword 中定义,然后在 UserSecurePassword 中重写(而不是doppleganged或duck-typed),Scala理解这是真正重新定义的方法 . 因此,当您在 SecurePassword 中混合时,它可以覆盖 User 中的 password() 方法 .

  • 0

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

    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)
    

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

  • 5

    我不确定这个用例是什么 . 为什么不在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

  • 1

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

    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的答案可能更接近您最初的要求

  • 1

    注意:关于错误消息,有待处理的issue 128 "no ambiguity error when inheriting conflicting member from java default method"

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

    SD-128修复默认方法的覆盖检查对于默认方法,继承两个冲突成员的检查是错误的,导致丢失错误消息 . 当覆盖默认方法时,我们也没有发出“needs`overtion'修饰符” .

相关问题