I have a simple case class Amount as below
case class Amount(value: Long, currency: Currency)
And an accompanying object to convert a string currency code into a Currency object
object Amount {
private val log = Logger(getClass)
def apply(value: Long, currencyCode: String) : Amount = {
try {
Amount(value, Currency.getInstance(currencyCode))
} catch {
case e: Exception =>
log.error(s"Invalid currency code [$currencyCode]")
throw new Exception(s"Invalid currency code [$currencyCode]")
}
}
}
Invocation :
val amount : Amount = Amount(1234, "USD")
When I read some data from the database, I have a custom parser such as
implicit val amountParser = Macro.parser[Amount]("value", "currencyCode")
However, the compiler complains
scala.ScalaReflectionException: value apply encapsulates multiple overloaded alternatives and cannot be treated as a method. Consider invoking `<offending symbol>.asTerm.alternatives` and manually picking the required method
[error] at scala.reflect.api.Symbols$SymbolApi$class.asMethod(Symbols.scala:228)
[error] at scala.reflect.internal.Symbols$SymbolContextApiImpl.asMethod(Symbols.scala:84)
[error] at anorm.Macro$.parserImpl(Macro.scala:70)
[error] at anorm.Macro$.namedParserImpl_(Macro.scala:25)
[error] implicit val amountParser = Macro.parser[Amount]("value", "currencyCode")
How do I make this work ?
UPDATE
After understanding the response from @MikeAllen, I decided to leave the case class Amount
and the object Amount
as is, instead I wrote a custom parser for the Amount as below
implicit private val amountParser = for {
value <- long("value")
currencyCode <- str("currency_code")
} yield {
Amount(value, currencyCode)
}
The Scala compiler will automatically generate an Amount.apply
factory method for creating case class
instances, which is why you're getting this error - because you have multiple Amount.apply
methods. One of these takes arguments of type ( Long
, Currency
) and the other takes arguments of type ( Long
, String
). The error message suggests that you need to select one of these from the overloaded alternatives reported through reflection .
Alternatively, your case class and companion might be reworked as follows:
final case class Amount(value: Long, currencyCode: String) {
/** Currency. Will create an exception on instantiation if code is invalid. */
val currency: Currency = {
try {
Currency.getInstance(currencyCode)
}
catch {
case e: Exception =>
Amount.log.error(s"Invalid currency code [$currencyCode]")
throw new Exception(s"Invalid currency code [$currencyCode]")
}
}
}
object Amount {
private val log = Logger(getClass)
}
This is not quite as elegant, admittedly, as you now have a field, currency
, that isn't one of the case class's parameters and that isn't available for pattern matching, while also carrying around the string form.
A better solution would be to keep your original case class
and convert the currency code field from a String
into a Currency
, before creating the Amount
instance, as part of the parser:
val amountMapping = {
get[Long]("value") ~ get[String]("currencyCode") map {
case value ~ currencyCode => {
val currency = {
try {
Currency.getInstance(currencyCode)
}
catch {
case e: Exception =>
Amount.log.error(s"Invalid currency code [$currencyCode]")
throw new Exception(s"Invalid currency code [$currencyCode]")
}
}
Amount(value, currency)
}
}
}
You can then use this to parse rows, for example with:
def amounts(): List[Amount] = SQL("select * from amounts").as(amountMapping *)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.