I wrote the following Scala code below to handle a String that I pass in, format the String, append it to a StringBuilder
and return the formatted String
with escaped unicode back to my caller for other processing.
The Scala compiler complains of the following on the lines where there is a String.format
call with the following error:
overloaded method value format with alternatives:
(x$1; java.util.Locale; x$2: String, X$3: Object*)
(x$1:String,x$2: Object*)
String cannot be applied to(*String, Int)
class TestClass {
private def escapeUnicodeStuff(input: String): String = {
//type StringBuilder = scala.collection.mutable.StringBuilder
val sb = new StringBuilder()
val cPtArray = toCodePointArray(input) //this method call returns an Array[Int]
val len = cPtArray.length
for (i <- 0 until len) {
if (cPtArray(i) > 65535) {
val hi = (cPtArray(i) - 0x10000) / 0x400 + 0xD800
val lo = (cPtArray(i) - 0x10000) % 0x400 + 0xDC00
sb.append(String.format("\\u%04x\\u%04x", hi, lo)) //**complains here**
} else if (codePointArray(i) > 127) {
sb.append(String.format("\\u%04x", codePointArray(i))) //**complains here**
} else {
sb.append(String.format("%c", codePointArray(i))) //**complains here**
}
}
sb.toString
}
}
How do I address this problem? How can I clean up the code to accomplish my purpose of formatting a String? Thanks in advance to the Scala experts here
The String.format
method in Java expects Objects
as its arguments. The Object
type in Java is equivalent to the AnyRef
type in Scala. The primitive types in Scala extend AnyVal
– not AnyRef
. Read more about the differences between AnyVal
, AnyRef
, and Any
in the docs or in this answer. The most obvious fix is to use the Integer
wrapper class from Java to get an Object
representation of your Ints
:
String.format("\\u%04x\\u%04x", new Integer(hi), new Integer(lo))
Using those wrapper classes is almost emblematic of unidiomatic Scala code, and should only be used for interoperability with Java when there is no better option. The more natural way to do this in Scala would be to either use the StringOps
equivalent method format
:
"\\u%04x\\u%04x".format(hi, lo)
You can also use the f
interpolator for a more concise syntax:
f"\\u$hi%04x\\u$lo%04x"
Also, using a for
loop like you have here is unidiomatic in Scala. You're better off using one of the functional list methods like map
, foldLeft
, or even foreach
together with a partial function using the match
syntax. For example, you might try something like:
toCodePointArray(input).foreach {
case x if x > 65535 =>
val hi = (x - 0x10000) / 0x400 + 0xD800
val lo = (x - 0x10000) % 0x400 + 0xDC00
sb.append(f"\\u$hi%04x\\u$lo%04x")
case x if > 127 => sb.append(f"\\u$x%04x")
case x => sb.append(f"$x%c")
}
Or, if you don't have to use StringBuilder
, which really only needs to be used in cases where you are appending many strings, you can replace your whole method body with foldLeft
:
def escapeUnicodeStuff(input: String) = toCodePointArray(input).foldLeft("") {
case (acc, x) if x > 65535 =>
val hi = (x - 0x10000) / 0x400 + 0xD800
val lo = (x - 0x10000) % 0x400 + 0xDC00
acc + f"\\u$hi%04x\\u$lo%04x"
case (acc, x) if x > 127 => acc + f"\\u$x%04x"
case (acc, x) => acc + f"$x%c"
}
Or a even map
followed by a mkString
:
def escapeUnicodeStuff(input: String) = toCodePointArray(input).map {
case x if x > 65535 =>
val hi = (x - 0x10000) / 0x400 + 0xD800
val lo = (x - 0x10000) % 0x400 + 0xDC00
f"\\u$hi%04x\\u$lo%04x"
case x if x > 127 => f"\\u$x%04x"
case x => f"$x%c"
}.mkString
I couldn't figure out what exactly is causing that "overload clash" but notice the code below:
scala> "\\u%04x\\u%04x".format(10,20)
res12: String = \u000a\u0014
Using the one provided by StringOps works.
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