Scala: конструктор с двумя паретнехами

Я новичок в скале. Что означает следующий синтаксис?

case class User(val id: Long)(val username: String)

Я читал о каррировании в scala, но объясните, пожалуйста, как это связано с конструкцией выше (если связано). Спасибо.


person Teimuraz    schedule 11.07.2016    source источник


Ответы (2)


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

scala> case class User(val id: Long)(val username: String)
defined class User

scala> val userBuilder = User(123L) _
userBuilder: String => User = <function1>

Обратите внимание на тип результирующего userBuilder — это функция от String (оставшийся параметр) до User.

Теперь, подобно частично примененным функциям, вы можете применить этот результат к String и получить экземпляр User:

scala> val user = userBuilder("a")
user: User = User(123)

scala> user.username
res1: String = a

Когда это полезно?

  1. Если вы хотите создать множество экземпляров с общими значениями для подмножества аргументов, например:

    case class Person(lastName: String)(val firstName: String)
    
    class Family(lastName: String, firstNames: List[String]) {
      def getMembers: List[Person] = {
        val creator = Person(lastName) _ // will be reused for each first name!
        firstNames.map(creator)
      }
    }
    
  2. Если вы хотите использовать один аргумент в качестве значения по умолчанию для другого:

    case class Measure(min: Int)(val max: Int = min*2)
    
    Measure(5)()   // max = 10
    Measure(5)(12) // default overridden, max = 12
    
  3. Если вы хотите использовать implicit аргументов, которые должны находиться в отдельном, последнем списке аргументов функции, как описано в Спецификация языка Scala (глава 7.2):

Метод или конструктор может иметь только один список неявных параметров, и это должен быть последний заданный список параметров.

person Tzach Zohar    schedule 11.07.2016

Он позволяет построить объект поэтапно.

val user = User(123L) _ // user now has only the ID

// later on
val completeUser = user("moreo") // user now also has the username

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

person mirosval    schedule 11.07.2016
comment
неточно - ваш ответ не компилируется, вы должны написать User(123L) _ , чтобы получить частично примененный конструктор. - person Tzach Zohar; 11.07.2016
comment
Также важно отметить, что тип user здесь не User, а функция: String => User - person Dima; 11.07.2016
comment
Спасибо вам за ответ. Я пытался сделать следующее: класс case User (valid id: Long, неявный репозиторий val: UserRepository), но у него есть ошибка компиляции, поэтому, когда я перешел на класс case User (val id: Long) (implicit val репозиторий: UserRepository) - работает. Но почему первый подход не работает. Свойство репозитория требуется для класса, и, как вы сказали, если я могу инициализировать пользовательский класс без второго параметра, этот класс будет несогласованным. Как решить эту проблему? - person Teimuraz; 11.07.2016
comment
@moreo, если вы перейдете на неявный, вы должны указать значение в контексте, и оно сразу же применяется во время первого вызова, а не в два этапа, как показано в моем ответе. Это допустимый, но другой вариант использования. - person mirosval; 11.07.2016