Конструктор перегрузки с другим типом параметра

Я знаю, что мы можем перегрузить конструктор класса в Scala следующим образом:

class Foo(x: Int, z: String) { 
  def this(z: String) = this(0, z);   
}

Но как я могу перегрузить класс, который имеет два совершенно разных типа параметров, как показано ниже (представьте, что я могу идентифицировать пользователя по имени или числовому идентификатору)

class User(userId: Int) {
  ...
}

class User(userName: String) {
  ...
}

3 ответа

(представьте, что я могу идентифицировать пользователя по имени или числовому идентификатору)

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

Один из способов сделать это - закодировать идентификатор пользователя с помощью встроенного в Scala Either тип:

class User private(identifier : Either[String, Int]) {
  def this(id : Int) = this(Right(id))
  def this(name : String) = this(Left(name))
}

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

trait UserIdentifier
object UserIdentifier {
  case class ById(id : Int) extends UserIdentifier
  case class ByName(name : String) extends UserIdentifier
}

class User(id : UserIdentifier) {
  def this(id : Int) = this(UserIdentifier.ById(id))
  def this(name : String) = this(UserIdentifier.ByName(name))
}

Делая это таким образом, вы предотвращаете проблемы, такие как попытка найти имя пользователя, которое вместо этого идентифицируется идентификатором. Второй подход также позволяет расширить идею UserIdentifier в будущем, в случае, если пользователь может быть идентифицирован каким-либо другим конструктом.

Или вы можете сделать это

object User {
  def apply(userId: Int) = new UserI(userId)
  def apply(name: String) = new UserS(name)

  class UserI(userId: Int)
  class UserS(userName: String)
}

И используйте это так:

  val u1 = User(1)
  val u2 = User("a")

Если у вас много общей логики, вы можете поместить ее в общий абстрактный класс

object User {
  def apply(userId: Int) = new UserI(userId)
  def apply(name: String) = new UserS(name)


  class UserI(userId: Int) extends AUser
  class UserS(userName: String) extends AUser

  abstract class AUser{
    // common logic for both classes
  }

}

Вы можете сделать это:

class User private() {
  def this( userName: String ) = { this(); ??? }
  def this( userId: Int ) = { this(); ??? }
}

private ключевое слово делает конструктор без аргументов закрытым. Это означает, что вашим другим вторичным конструкторам не нужно ничего передавать первичному конструктору (фактически делая эти два вторичных конструктора независимыми), но вызывающие по-прежнему не могут создавать экземпляры класса без передачи какого-либо параметра. Обратите внимание, что этот шаблон может быть сложно использовать, когда у вашего класса есть значения для инициализации из параметров construtors.

Другие вопросы по тегам