Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala - override a class method in a trait

I'm new to Scala (came from Ruby world).

And I was curious about "traits" concept in Scala (which should be ~similar to modules in ruby, if I understand it correctly).

And here's a use case.

Suppose I have a class called User defined like this:

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

And suppose I have a trait SecurePasswords using which I would like to "override" the password method defined in the User class.

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

And, suppose, I want it to be applicable to instances of the User class and not to the entire class itself.

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

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

Now this is an ideal output that I would expect, however, I get different errors like "anonymous class inherits conflicting members ... (Note: this can be resolved declaring etc etc ...)"

Can this be done in Scala or I'm asking too much / doing something really weird? Is it possible to do w/o any additional class definitions, like UserWithSecurePassword extends User

Thank you all in advance!

P.S In case you are wondering "why?", just assume that system would contain a lot of entities that require password (and, potentially, secure password), so the trait could be used in a lot of places.

like image 357
Dmitri Avatar asked Nov 07 '14 22:11

Dmitri


3 Answers

The conflict between the two method definitions is because you didn't make them the "same method". All you did was make them coincidentally have the same name, arguments, and return type.

To make them really the same method, so that one can override the other, define them in the same place:

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

By being defined in the trait HasPassword, and then overridden (instead of doppleganged or duck-typed) in User and SecurePassword, Scala understands that this is truly the same method being redefined. Thus, when you mix in SecurePassword, it can override the password() method in User.

like image 163
Dan Getz Avatar answered Sep 23 '22 23:09

Dan Getz


In addition to my previous answer - a completely different way to get what you want is to pass the password function into the User class, rather than using 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)

As shown, you can use a default argument to avoid having to specify the password function in the default case.

like image 33
DNA Avatar answered Sep 23 '22 23:09

DNA


I'm not sure what the use case would be for this. Why not just have the User, in Ruby terms, 'mixin', the SecurePasswords trait and override password within it's class definition?

Coming from Ruby, this might be more difficult to get, but Scala is a compiled language and it's generally not a good idea to change class definitions dynamically/on-the-fly like this. Think of the type system in Scala as a way of testing your code. The more you defer code interpretation to runtime, the less testable/safe your code becomes. This is one of the strengths of Scala/Java/Haskell/... (insert compiled typed language) - the type system can catch a lot of errors at compile time. Use this to your advantage, don't fight against it.

I would look into the use of implicit parameters and how they can relate/be used with traits.

Also, without the broader context of what you are trying to accomplish in your code with this pattern, it's hard to know, but this link might prove useful to you if you are trying to implement some sort of adapter pattern.

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

like image 30
josiah Avatar answered Sep 24 '22 23:09

josiah