Scala - how to pattern match when chaining two implicit conversions? - java

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).

Related

Signature of whenComplete from CompletableFuture

Does anyone know to translate the whenComplete method of java CompletableFuture to Scala? I really don't know how to do it and I'm kinda' stuck. Thank you
Here's an example that shows you how it might work (inside the scala REPL)...
$ scala
Welcome to Scala 2.12.6 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_181).
Type in expressions for evaluation. Or try :help.
scala> import java.util.concurrent.CompletableFuture
import java.util.concurrent.CompletableFuture
Create a future that waits for 5 seconds, then completes returning a string value. (See also the note below explaining how this works.)
scala> val future = CompletableFuture.supplyAsync {() =>
| Thread.sleep(5000) // Wait 5,000 ms (5 seconds).
| "We done did it!"
| }
future: java.util.concurrent.CompletableFuture[String] = java.util.concurrent.CompletableFuture#5a466dd[Not completed]
Now have some code execute when the future has finished. (This is where you should begin with your own whenComplete implementation.)
scala> future.whenComplete {(result, error) =>
| println(s"Result was: '$result', error was: '$error'")
| }
Result was 'We done did it!'; error was 'null'
res0: java.util.concurrent.CompletableFuture[String] = java.util.concurrent.CompletableFuture#3ea9a091[Completed normally]
(Note that, in this case, the future completed before I was able to type in the whenComplete method. This is clearly a race condition, so if you paste the whole lot into the REPL at once, you may see that res0 is determined to have "Not completed", and then see the output of the whenComplete function.)
So what's going on here, because this code doesn't look much like the JavaDoc for the associated classes?
It's a little Scala magic called single abstract methods. Essentially, if an asbtract class (or trait) has a single abstract method, then you can replace an instance of that class with the definition of the abstract method. Further, Scala knows which class is relevant from the argument list of the associated function.
Let's start with CompletableFuture.supplyAsync, which takes a single Supplier[T] argument. In Scala terms, this type looks like this:
trait Supplier[T] {
def get(): T
}
So we could have written the creation of the future element as follows instead:
scala> import java.util.function.Supplier
import java.util.function.Supplier
scala> val future = CompletableFuture.supplyAsync {
| new Supplier[String] {
| override def get(): String = {
| Thread.sleep(5000) // Wait 5,000 ms (5 seconds).
| "We done did it!"
| }
| }
| }
future: java.util.concurrent.CompletableFuture[String] = java.util.concurrent.CompletableFuture#35becbd4[Not completed]
Because the Scala compiler knows that supplyAsync takes a Supplier[T], and because Supplier[T] has a single abstract method, get, the compiler is able to accept the abbreviated form that uses a function literal as the definition of both Supplier and its get method.
We then use the same approach with the whenComplete method. Here, the type of argument is a BiConsumer[T, U] (where T is the type of value returned by the future, and U is an exception type), which takes a single abstract method accept. (This type also has an andThen method, but that's not abstract, so it doesn't matter to Scala.) So, to have been more explicit, we could have written the following:
scala> import java.util.function.BiConsumer
scala> future.whenComplete {
| new BiConsumer[String, Throwable] {
| override def accept(result: String, error: Throwable): Unit = {
| println(s"Result was: '$result', error was: '$error'")
| }
| }
| }
Result was 'We done did it!'; error was 'null'
res0: java.util.concurrent.CompletableFuture[String] = java.util.concurrent.CompletableFuture#3ea9a091[Completed normally]
Both approaches are valid, so feel free to use whichever one makes sense to you...
Note that whenComplete is a lot uglier than conventional Scala code typically requires: If the future threw an exception instead of successfully finishing, then error will be non-null; otherwise, result will contain the result of the future, which could also be null.
If at all possible, I would strongly recommend using Scala Futures instead. Far more functional, and far more elegant than those in Java.
You can convert a Java CompletableFuture into a Scala Future with the following implicit conversion:
import scala.concurrent.{Future, Promise}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.language.implicitConversion
import scala.util.{Failure, Success}
implicit def completableFutureToFuture[T](cf: CompletableFuture[T]): Future[T] = {
val p = Promise[T]() // Promise monitoring result of java cf.
// Handle completion of Java future.
cf.whenComplete {(result, error) =>
// If error is not null, then future failed.
if(error ne null) p.failure(error)
// Otherwise, it succeeded with result.
else p.success(result)
}
// Return the Scala future associated with the promise.
p.future
}
You can then handle completion of the Java future far more elegantly (again, in the REPL, with the above defined):
scala> val scalaFuture = future // Implicit conversion called.
scalaFuture: scala.concurrent.Future[String] = Future(Success(We done did it!))
scala> scalaF.onComplete {
| case Success(s) => println(s"Succeeded with '$s'")
| case Failure(e) => println(s"Failed with '$e'...")
| }

convert java to scala code - change of method signatures

Trying to convert some java to scala code I face the problem of a different method signature which compiled fine in the java world:
The following code in java (from https://github.com/DataSystemsLab/GeoSpark/blob/master/babylon/src/main/java/org/datasyslab/babylon/showcase/Example.java#L122-L126)
visualizationOperator = new ScatterPlot(1000,600,USMainLandBoundary,false,-1,-1,true,true);
visualizationOperator.CustomizeColor(255, 255, 255, 255, Color.GREEN, true);
visualizationOperator.Visualize(sparkContext, spatialRDD);
imageGenerator = new SparkImageGenerator();
imageGenerator.SaveAsFile(visualizationOperator.distributedVectorImage, "file://"+outputPath,ImageType.SVG);
Is translated to https://github.com/geoHeil/geoSparkScalaSample/blob/master/src/main/scala/myOrg/visualization/Vis.scala#L45-L57
val vDistributedVector = new ScatterPlot(1000, 600, USMainLandBoundary, false, -1, -1, true, true)
vDistributedVector.CustomizeColor(255, 255, 255, 255, Color.GREEN, true)
vDistributedVector.Visualize(s, spatialRDD)
sparkImageGenerator.SaveAsFile(vDistributedVector.distributedVectorImage, outputPath + "distributedVector", ImageType.SVG)
Which will throw the following error:
overloaded method value SaveAsFile with alternatives:
[error] (x$1: java.util.List[String],x$2: String,x$3: org.datasyslab.babylon.utils.ImageType)Boolean <and>
[error] (x$1: java.awt.image.BufferedImage,x$2: String,x$3: org.datasyslab.babylon.utils.ImageType)Boolean <and>
[error] (x$1: org.apache.spark.api.java.JavaPairRDD,x$2: String,x$3: org.datasyslab.babylon.utils.ImageType)Boolean
[error] cannot be applied to (org.apache.spark.api.java.JavaPairRDD[Integer,String], String, org.datasyslab.babylon.utils.ImageType)
[error] sparkImageGenerator.SaveAsFile(vDistributedVector.distributedVectorImage, outputPath + "distributedVector", ImageType.SVG)
Unfortunately, I am not really sure how to fix this / how to properly call the method in scala.
This is a problem in ImageGenerator, inherited by SparkImageGenerator. As you can see here, it has a method
public boolean SaveAsFile(JavaPairRDD distributedImage, String outputPath, ImageType imageType)
which uses a raw type (JavaPairRDD without <...>). They exist primarily for compatibility with pre-Java 5 code and shouldn't normally be used otherwise. For this code, there is certainly no good reason, as it actually expects specific type parameters. Using raw types merely loses type-safety. Maybe some subclasses (current or potential) might override it and expect different type parameters, but this would be a misuse of inheritance and there must be a better solution.
Scala doesn't support raw types in any way and so you can't call this method from it (AFAIK). As a workaround, you could write a wrapper in Java which used correct types and call this wrapper from Scala. I misremembered, it's extending Java classes extending raw types which was impossible, and even then there are workarounds.
You might be able to call it by explicit type ascription (preferable to casting):
sparkImageGenerator.SaveAsFile(
(vDistributedVector.distributedVectorImage: JavaPairRDD[_, _]),
outputPath + "distributedVector", ImageType.SVG)
But given the error message shows just JavaPairRDD, I don't particularly expect it to work. If this fails, I'd still go with a Java wrapper.
The accepted answer is correct in saying that raw types should be avoided. However Scala can interoperate with Java code that has raw types. Scala interprets the raw type java.util.List as the existential type java.util.List[_].
Take for example this Java code:
// Test.java
import java.util.Map;
public class Test {
public boolean foo(Map map, String s) {
return true;
}
}
Then try to call it from Scala:
Welcome to Scala 2.12.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_131).
Type in expressions for evaluation. Or try :help.
scala> import java.util.{Map,HashMap}
import java.util.{Map,HashMap}
scala> new Test().foo(new HashMap[String,Integer], "a")
res0: Boolean = true
scala> val h: Map[_,_] = new HashMap[String,Integer]
h: java.util.Map[_, _] = {}
scala> new Test().foo(h, "a")
res1: Boolean = true
So it looks like there must be some other problem.

How To Convert Scala Case Class to Java HashMap

I'm using Mule ESB (Java Based) and I have some scala components that modify and create data. My Data is represented in Case Classes. I'm trying to convert them to Java, however Just getting them to convert to Scala types is a challenge. Here's a simplified example of what I'm trying to do:
package com.echostar.ese.experiment
import scala.collection.JavaConverters
case class Resource(guid: String, filename: String)
case class Blackboard(name: String, guid:String, resource: Resource)
object CCC extends App {
val res = Resource("4alskckd", "test.file")
val bb = Blackboard("Test", "123asdfs", res)
val myMap = getCCParams(bb)
val result = new java.util.HashMap[String,Object](myMap)
println("Result:"+result)
def getCCParams(cc: AnyRef) =
(Map[String, Any]() /: cc.getClass.getDeclaredFields) {(a, f) =>
f.setAccessible(true)
val value = f.get(cc) match {
// this covers tuples as well as case classes, so there may be a more specific way
case caseClassInstance: Product => getCCParams(caseClassInstance): Map[String, Any]
case x => x
}
a + (f.getName -> value)
}
}
Current Error: Recursive method needs return type.
My Scala Foo isn't very strong. I grabbed this method from another answer here
and basically know what it's doing, but not enough to change this to java.util.HashMap and java.util.List
Expected Output:
Result:{"name"="Test", "guid"="123asdfs", "resource"= {"guid"="4alskckd", "filename"="test.file"}}
UPDATE1:
1. Added getCCParams(caseClassInstance): Map[String, Any] to line 22 Above per #cem-catikkas. IDE syntax error still says "recursive method ... needs result type" and "overloaded method java.util.HashMap cannot be applied to scala.collection.immutable.Map".
2. Changed java.util.HashMap[String, Object]
You should follow what the error tells you. Since getCCParams is a recursive method you need to declare its return type.
def getCCParams(cc: AnyRef): Map[String, Any]
Answering this in case anyone else going through the issue ends up here (as happened to me).
I believe the error you were getting had to do with the fact that the return type was being declared at method invocation (line 22), however the compiler was expecting it at the method's declaration (in your case, line 17). The below seems to have worked:
def getCCParams(cc: AnyRef): Map[String, Any] = ...
Regarding the conversion from Scala Map to Java HashMap, by adding the ._ wildcard to the JavaConverters import statement, you manage to import all the methods of the object as single identifiers, which is a requirement for implicit conversions. This will include the asJava method which can then be used to convert the Scala Map to a Java one, and then this can be passed to the java.util.HashMap(Map<? extends K,? extends V> m) constructor to instantiate a HashMap:
import scala.collection.JavaConverters._
import java.util.{HashMap => JHashMap}
...
val myMap = getCCParams(bb)
val r = myMap.asJava // converting to java.util.Map[String, Any]
val result: JHashMap[String,Any] = new JHashMap(r)
I wonder if you've considered going at it the other way around, by implementing the java.util.Map interface in your case class? Then you wouldn't have to convert back and forth, but any consumers downstream that are using a Map interface will just work (for example if you're using Groovy's field dot-notation).

How do I write a JSON Format for an object in the Java library that doesn't have an apply method?

I've been stuck on this particular problem for about a week now, and I figure I'm going to write this up as a question on here to clear out my thoughts and get some guidance.
So I have this case class that has a java.sql.Timestamp field:
case class Request(id: Option[Int], requestDate: Timestamp)
and I want to convert this to a JsObject
val q = Query(Requests).list // This is Slick, a database access lib for Scala
printList(q)
Ok(Json.toJson(q)) // and this is where I run into trouble
"No Json deserializer found for type List[models.Request]. Try to implement an implicit Writes or Format for this type." Okay, that makes sense.
So following the Play documentation here, I attempt to write a Format...
implicit val requestFormat = Json.format[Request] // need Timestamp deserializer
implicit val timestampFormat = (
(__ \ "time").format[Long] // error 1
)(Timestamp.apply, unlift(Timestamp.unapply)) // error 2
Error 1
Description Resource Path Location Type overloaded method value format with alternatives:
(w: play.api.libs.json.Writes[Long])(implicit r: play.api.libs.json.Reads[Long])play.api.libs.json.OFormat[Long]
<and>
(r: play.api.libs.json.Reads[Long])(implicit w: play.api.libs.json.Writes[Long])play.api.libs.json.OFormat[Long]
<and>
(implicit f: play.api.libs.json.Format[Long])play.api.libs.json.OFormat[Long]
cannot be applied to (<error>, <error>)
Apparently importing like so (see the documentation "ctrl+F import") is getting me into trouble:
import play.api.libs.json._ // so I change this to import only Format and fine
import play.api.libs.functional.syntax._
import play.api.libs.json.Json
import play.api.libs.json.Json._
Now that the overloading error went away, I reach more trubbles: not found: value __ I imported .../functional.syntax._ already just like it says in the documentation! This guy ran into the same issue but the import fixed it for him! So why?! I thought this might just be Eclipse's problem and tried to play run anyway ... nothing changed. Fine. The compiler is always right.
Imported play.api.lib.json.JsPath, changed __ to JsPath, and wallah:
Error 2
value apply is not a member of object java.sql.Timestamp
value unapply is not a member of object java.sql.Timestamp
I also try changing tacks and writing a Write for this instead of Format, without the fancy new combinator (__) feature by following the original blog post the official docs are based on/copy-pasted from:
// I change the imports above to use Writes instead of Format
implicit val timestampFormat = new Writes[Timestamp]( // ERROR 3
def writes(t: Timestamp): JsValue = { // ERROR 4 def is underlined
Json.obj(
/* Returns the number of milliseconds since
January 1, 1970, 00:00:00 GMT represented by this Timestamp object. */
"time" -> t.getTime()
)
}
)
ERROR 3: trait Writes is abstract, cannot be instantiated
ERROR 4: illegal start of simple expression
At this point I'm about at my wits' end here, so I'm just going back to the rest of my mental stack and report from my first piece of code
My utter gratefulness to anybody who can put me out of my coding misery
It's not necessarily apply or unapply functions you need. It's a) a function that constructs whatever the type you need given some parameters, and b) a function that turns an instance of that type into a tuple of values (usually matching the input parameters.)
The apply and unapply functions you get for free with a Scala case class just happen to do this, so it's convenient to use them. But you can always write your own.
Normally you could do this with anonymous functions like so:
import java.sql.Timestamp
import play.api.libs.functional.syntax._
import play.api.libs.json._
implicit val timestampFormat: Format[Timestamp] = (
(__ \ "time").format[Long]
)((long: Long) => new Timestamp(long), (ts: Timestamp) => (ts.getTime))
However! In this case you fall foul of a limitation with the API that prevents you from writing formats like this, with only one value. This limitation is explained here, as per this answer.
For you, a way that works would be this more complex-looking hack:
import java.sql.Timestamp
import play.api.libs.functional.syntax._
import play.api.libs.json._
implicit val rds: Reads[Timestamp] = (__ \ "time").read[Long].map{ long => new Timestamp(long) }
implicit val wrs: Writes[Timestamp] = (__ \ "time").write[Long].contramap{ (a: Timestamp) => a.getTime }
implicit val fmt: Format[Timestamp] = Format(rds, wrs)
// Test it...
val testTime = Json.obj("time" -> 123456789)
assert(testTime.as[Timestamp] == new Timestamp(123456789))

Casting objects in JRuby

Is there a way I can explicitly cast one Java object to another Java class from JRuby?
Sometimes I want to be able to invoke SomeJavaClass#aMethod(MySuperClass) rather than SomeJavaClass#aMethod(MyClass) from JRuby.
From Java, I'd do this:
someJavaObject.aMethod( (MySuperClass) myObj );
but I didn't see a #cast ruby method or anything like that to do the equivalent from JRuby.
Note that the question Casting Java Objects From JRuby lacks an answer for the general case, which is why I'm re-asking the question.
You need to make use of either the #java_send or #java_alias feature available starting with JRuby 1.4 to select the method you wish to call. Example:
class Java::JavaUtil::Arrays
boolean_array_class = [false].to_java(:boolean).java_class
java_alias :boolean_equals, :equals, [boolean_array_class, boolean_array_class]
end
a1 = [false, true]
Java::JavaUtil::Arrays.boolean_equals a1, a1
# => TypeError: for method Arrays.equals expected [class [Z, class [Z]; got: [org.jruby.RubyArray,org.jruby.RubyArray]; error: argument type mismatch
Java::JavaUtil::Arrays.boolean_equals a1.to_java(:boolean), a1.to_java(:boolean)
# => true
a2 = [true, false]
Java::JavaUtil::Arrays.boolean_equals a1.to_java(:boolean), a2.to_java(:boolean)
# => false

Categories

Resources