6

I'm using a ConcurrentHashMap in Scala and I would like to use the computeIfAbsent() method but can't figure out the syntax for the second argument. Can someone show me what would be the proper syntax?

When running the following code

val data = new ConcurrentHashMap[String, LongAdder]

data.computeIfAbsent("bob", k: String => new LongAdder()).increment()

I'm getting the following error

Type mismatch, expected: Function[_ >: String, _ <: LongAdder], actual: (String) => Any

Thanking you in advance

Francis

2 Answers 2

10

The problem is that you're using java.util.concurrent.ConcurrentHashMap, which accepts java.util.function.Function as a parameter for computeIfAbsent() instead of scala.Function1 which you pass to it.

Since scala doesn't support lambda conversion for functional interfaces as Java does (at least not without the -Xexperimental flag), you can solve this by implementing a java.util.function.Function explicitly:

val data = new ConcurrentHashMap[String, LongAdder]
val adderSupplier = new java.util.function.Function[String, LongAdder]() {
  override def apply(t: String): LongAdder = new LongAdder()
}
data.computeIfAbsent("bob", adderSupplier).increment()

Alternatively, if you need this more often, you may write a utility conversion function or even an implicit conversion:

object FunctionConverter {
  implicit def scalaFunctionToJava[From, To](function: (From) => To): java.util.function.Function[From, To] = {
    new java.util.function.Function[From, To] {
      override def apply(input: From): To = function(input)
    }
  }
}

import FunctionConverter._
val data = new ConcurrentHashMap[String, LongAdder]()
data.computeIfAbsent("bob", (k: String) => new LongAdder()) // <- implicit conversion applied here
0
2

If you enable -Xexperimental flag you can use scala anonymous function notation for this:

scala> val data = new java.util.concurrent.ConcurrentHashMap[String, Int]
data: java.util.concurrent.ConcurrentHashMap[String,Int] = {}

scala> data.computeIfAbsent("bob", _.size)
res0: Int = 3

Note that you still can't pass regular scala Function

scala> val f: String => Int = _.size
f: String => Int = <function1>

scala> data.computeIfAbsent("bob", f)
<console>:13: error: type mismatch;
 found   : String => Int
 required: java.util.function.Function[_ >: String, _ <: Int]
       data.computeIfAbsent("bob", f)
                               ^

But eta-expansion will work

scala> def a(s: String): Int = s.size
a: (s: String)Int

scala> data.computeIfAbsent("bob", a)
res3: Int = 3

Not the answer you're looking for? Browse other questions tagged or ask your own question.