我是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如果您想知道"为什么?",只是假设系统包含许多需要密码的实体(可能还有安全密码),因此可以在很多地方。
答案 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
中定义,然后在User
和SecurePassword
中覆盖(而不是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
方法默认情况下,继承两个冲突成员的检查是错误的 方法,导致丢失错误消息。
我们也没有发出"需要`覆盖'改性剂"当覆盖一个 默认方法。