I wrote a method to parse Metrics data and at first faced a problem with the type of transactionMap which is a java.util.Map
. And I solved it using JavaConverters.
def parseMetrics(metric: Metric) = {
import scala.collection.JavaConverters._
metric.transactionMap.asScala.values.map {
case false => "N"
case true => "Y"
}.toList
But after that I got an error while pattern matching true and false values: pattern type is incompatible with expected type, found: Boolean, required: java.lang.Boolean
As far as I understand Scala does not chain two implicit conversions. Is there a way to fix it using JavaConverters?
The other answer provides a reasonable way to solve this problem, but doesn't show why you're running into it or how the approach it proposes works.
The Scala standard library does provide an implicit conversion from java.lang.Boolean
to scala.Boolean
, which you can see in action by using reify
in a REPL to desugar some code that uses a Java boolean in a context where a Scala boolean is expected:
scala> val x: java.lang.Boolean = true
x: Boolean = true
scala> import scala.reflect.runtime.universe.reify
import scala.reflect.runtime.universe.reify
scala> reify(if (x) 1 else 0)
res0: reflect.runtime.universe.Expr[Int] =
Expr[Int](if (Predef.Boolean2boolean($read.x))
1
else
0)
The problem is that simply trying to match a java.lang.Boolean
value against true
or false
isn't sufficient to trigger the conversion. You can check this by defining your own types where you can be sure you know exactly what conversions are in play:
scala> case class Foo(i: Int); case class Bar(i: Int)
defined class Foo
defined class Bar
scala> implicit def foo2bar(foo: Foo): Bar = Bar(foo.i)
foo2bar: (foo: Foo)Bar
scala> Foo(100) match { case Bar(x) => x }
<console>:17: error: constructor cannot be instantiated to expected type;
found : Bar
required: Foo
Foo(100) match { case Bar(x) => x }
^
This is a language design decision. It would probably be possible to have the implicit conversions applied in these scenarios, but there's also probably a good reason that they aren't (off the top of my head I'm not familiar with any relevant discussions or issues, but that doesn't mean they don't exist).
The reason Andy's solution works is that the java.lang.Boolean
is in a position where the compiler expects a scala.Boolean
(a condition) and is willing to apply the Predef.Boolean2boolean
conversion. You could do this manually if you really wanted to:
def parseMetrics(metric: Metric) = {
import scala.collection.JavaConverters._
metric.transactionMap.asScala.values.map(Predef.Boolean2boolean).map {
case false => "N"
case true => "Y"
}.toList
}
…but to my eye at least pattern matching on Boolean
is a little clunkier than using a conditional.
Use if/else rather than a match statement for Boolean checking:
def parseMetrics(metric: Metric) = {
import scala.collection.JavaConverters._
metric.transactionMap.asScala.values.map {
x => if (x) "Y" else "N"
}.toList
My suspicion is that within the if statement the java.lang.Boolean
(which I presume x
is here) can be coerced to Boolean
via import scala.collection.JavaConverters._
... but the match statement doesn't do the same coercion, but would have to be made explicitly (or match on the java.lang.Boolean
values).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With