Scala-переопределение метода класса в чертеже

Я новичок в Scala (пришел из Ruby world).

и мне было интересно понятие "черты" в 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 в случае, если вам интересно "почему?", просто предположим, что система будет содержать много сущностей, которые требуют пароля (и, потенциально, безопасного пароля), поэтому черта может использоваться во многих местах.

5 ответов


конфликт между двумя определениями методов заключается в том, что вы не сделали их "одним и тем же методом". Все, что вы сделали, это сделали их совпадающими с тем же именем, аргументами и типом возврата.

чтобы сделать их действительно одним и тем же методом, чтобы один мог переопределить другой, определите их в том же месте:

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, а затем переопределяется (вместо doppleganged или duck-typed) в User и SecurePassword, Scala понимает, что это действительно тот же метод, который переопределяется. Таким образом, когда вы смешиваете в SecurePassword, он может переопределить password() метод 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)

как показано, можно использовать аргумент по умолчанию, чтобы избежать необходимости указывать функцию пароля в случае по умолчанию.


Я не уверен, какой вариант использования будет для этого. Почему бы просто не иметь пользователя, в терминах 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


вы можете предоставить различное поведение пароля для каждого экземпляра следующим образом-но вам нужно предоставить явную черту в каждом случае (по умолчанию или безопасно):

  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)

Update: тем не менее, я думаю, что ответ Дэна Гетца, вероятно, ближе к тому, что вы изначально просили


примечание: что касается сообщения об ошибке, есть до выпуск 128 "нет ошибки неоднозначности при наследовании конфликтующего члена от метода java по умолчанию"

он должен быть разрешен в scala 2.12.х совершить 3a3688f64

SD-128 исправить переопределение проверки для default методы

проверка наследования двух конфликтующих членов была неправильной по умолчанию методы, приводящие к отсутствию сообщения об ошибке.

мы также не выдавали модификатор "needs" override " при переопределении метод по умолчанию.