Scala - 覆盖特征中的类方法

时间:2014-11-07 22:49:09

标签: java scala inheritance override traits

我是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

提前谢谢大家!

PS如果您想知道"为什么?",只是假设系统包含许多需要密码的实体(可能还有安全密码),因此可以在很多地方。

5 个答案:

答案 0 :(得分: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时,它可以覆盖password()中的User方法。

答案 1 :(得分:5)

除了我之前的回答 - 一种完全不同的方式来获得你想要的是将密码函数传递给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)

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

答案 2 :(得分:1)

我不确定这个用例是什么。为什么不让用户,用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

答案 3 :(得分: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的答案可能更接近你原来的要求

答案 4 :(得分:0)

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

它应该在commit 3a3688f64

的scala 2.12.x中解决
  

SD-128修复了对default方法

的覆盖检查      

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

     

我们也没有发出"需要`覆盖'改性剂"当覆盖一个   默认方法。