Question

I didn't want to write alot of boilerplate code, so I decided to write generic method for lazy-init.

import java.util._
import concurrent.ConcurrentHashMap

object GenericsTest {
  val cache: ConcurrentHashMap[Long, 
    ConcurrentHashMap[Long, 
      ConcurrentHashMap[Long, 
        ConcurrentHashMap[Long, Long]]]] = new ConcurrentHashMap()

  def main(args: Array[String]) {
    val x = get(cache, 1)(() => new ConcurrentHashMap())
    val y = get(x, 1)(() => new ConcurrentHashMap())
    val z = get(y, 1)(() => new ConcurrentHashMap())
  }

  def get[B, A](map: ConcurrentHashMap[A, B], a: A)(factory: () => B): B = {
    if (map.containsKey(a)) {
      map.get(a)
    } else {
      val b = factory()
      map.put(a, factory())
      b
    }
  }
}

This example is only running with hard-coded Long but not with generic A, what can be the problem? Maybe there is another way to do such things?

Was it helpful?

Solution

The error is in this line:

val x = get(cache, 1)(() => new ConcurrentHashMap())

The problem is that the type of 1 is Int.

We have this method signature:

get[A, B](map: ConcurrentHashMap[A, B], a: A)(factory: () => B): B

The types of parameters passed in that problematic call are (B is that long nested type, it's irrelevant now):

ConcurrentHashMap[Long, B] and Int

So the compiler calculates that A must be the closest common ancestor of Long and Int, which is AnyVal, so finally it will use the passed parameters with the types of:

ConcurrentHashMap[AnyVal, B] and AnyVal

But ConcurrentHashMap is invariant in it's first type parameter, so the cache val can't be used as ConcurrentHashMap[AnyVal, B], so the compiler gives this error message (removed the long nested type parameter part, it doesn't matter now):

found   : java.util.concurrent.ConcurrentHashMap[Long, ...]
required: java.util.concurrent.ConcurrentHashMap[AnyVal, ...]
Note: Long <: AnyVal, but Java-defined class ConcurrentHashMap is invariant in type K.

To fix this you need to pass the second parameter as Long:

val x = get(cache, 1L)(() => new ConcurrentHashMap())
val y = get(x, 1L)(() => new ConcurrentHashMap())
val z = get(y, 1L)(() => new ConcurrentHashMap())
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top