Вспомогательный конструктор с разными параметрами

#scala #constructor

#scala #конструктор

Вопрос:

Есть ли способ определить вспомогательный конструктор, используя совершенно другой набор параметров, чем основной конструктор?

Пример — давайте создадим класс Customer:

 import org.apache.spark.sql.Row

case class Customer( id : Int, revenue : Double) {
  
  //auxiliary constructor 
  def this(r : Row) = {
    this(r.getAs[Int]("CustomerId"), r.getAs[Double]("InvoiceTotal"))
  }
}
 

Хотя приведенное выше определение компилируется просто отлично, я не могу вызвать альтернативный конструктор из кода. Приведенный ниже пример вызывает ошибку компиляции not enough arguments for method apply: (id, revenue)... .

 val newCustomer = customer(currentRow)
 

Все примеры, которые я нашел здесь на SO или других страницах (например, geeks для geeks), всегда показывают вспомогательные конструкторы с параметрами, которые являются супернабором основных параметров конструктора по умолчанию. Как я мог бы создавать конструкторы, которые соответствовали бы сценарию из моего примера выше?

Комментарии:

1. Подсказка: прочитайте сообщение об ошибке. Действительно ли это говорит о том, что вы «не можете вызвать альтернативный конструктор из кода», как вы утверждаете? На самом деле, говорит ли это что-нибудь о конструкторах вообще ? Подсказка № 2: посмотрите, как вызвать конструктор.

2. Вместо вспомогательного конструктора, который не очень распространен, вы можете добавить вспомогательную фабрику в сопутствующий объект. — кстати, если вы спросите меня, вам не следует использовать распределенный движок, если вы даже не понимаете, как работают классы case.

3. @<Луис Мигель Мехия Суарес> хотите объяснить подробнее? Особенно в отношении ответов ниже?

Ответ №1:

То, как вы определяете свой вспомогательный конструктор, прекрасно. Параметры вспомогательного конструктора не должны перекрываться с параметрами основного конструктора (или быть подмножеством).

Однако, чтобы создать экземпляр вашего класса case с помощью вспомогательного конструктора, вам нужно будет использовать new (как если бы это был обычный класс), иначе экземпляр по умолчанию apply будет создан с помощью основного конструктора.

 import org.apache.spark.sql.Row

case class Customer(id: Int, revenue: Double) {  
  def this(r: Row) =
    this(r.getAs[Int]("CustomerId"), r.getAs[Double]("InvoiceTotal"))
}

val df = Array((1, 1000.0), (2, 2000.0)).toDF("CustomerId", "InvoiceTotal")

df.map(row => new Customer(row)).printSchema
// root
//  |-- id: integer (nullable = false)
//  |-- revenue: double (nullable = false)
 

Ответ №2:

Customer является классом case, что означает, что упрощенный конструктор Customer(currentRow) пытается вызвать этот код в сопутствующем объекте…

 object Customer {
  def apply(id:Int, revenue:Double):Customer = new Customer(id, revenue)
}
 

…который обходит вспомогательный конструктор.

Попробуйте new Customer(currentRow) и посмотрите, работает ли это для вас.

Комментарии:

1. спасибо вам за ваше предложение. Использование object работает нормально (у меня это было в моем проекте, пока я не нашел ответ от Leo C), но я надеялся сохранить все поведение класса в рамках его основного определения, если это возможно. Вот почему отмечаю ответ Лео Си для ответа.

2. Спасибо за дальнейшее объяснение — теперь я понимаю, что вы имели в виду, и был введен в заблуждение моим собственным обходным путем. Тем не менее, ответ Лео дает более понятное объяснение и пример, поэтому, пожалуйста, позвольте мне сохранить его в качестве принятой версии.