#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. Спасибо за дальнейшее объяснение — теперь я понимаю, что вы имели в виду, и был введен в заблуждение моим собственным обходным путем. Тем не менее, ответ Лео дает более понятное объяснение и пример, поэтому, пожалуйста, позвольте мне сохранить его в качестве принятой версии.