4

I am pretty new to Scala and still do not understand generics that well. Consequently, I cannot figure out why the compiler hates me with type mismatch errors.

I am using Google's Guava library to create some simple caches represented as Scala's ConcurrentMap. I would like to keep track of the created caches using another Map of cache name to ConcurrentMap (the cache). Here is what I have so far which compiles but is missing the tracking of the caches (I've commented out the bits that fail):

import scala.collection.mutable.ConcurrentMap

trait CacheManager {

    def getCache[V <: AnyRef](
            cacheName: String,
            cacheListener: Option[CacheListener] = None): ConcurrentMap[String, V]

}


import scala.collection.JavaConversions._
import com.google.common.collect.MapMaker
import java.util.concurrent.{ConcurrentMap => JConcurrentMap, TimeUnit}
import org.slf4j.LoggerFactory
import com.google.common.cache.{RemovalNotification, RemovalListener, CacheBuilder}
import scala.collection.mutable.ConcurrentMap

class LocalCacheManager extends CacheManager {

    private val logger = LoggerFactory.getLogger(classOf[LocalCacheManager])


    private val caches /*: ConcurrentMap[String, ConcurrentMap[String, _ <: AnyRef]]*/ =
            asScalaConcurrentMap[String, ConcurrentMap[String, _ <: AnyRef]](
                new MapMaker().concurrencyLevel(4).makeMap[String, ConcurrentMap[String, _ <: AnyRef]]())

    def getCache[V <: AnyRef](cacheName: String, cacheListener: Option[CacheListener] = None) = {
//        caches.getOrElseUpdate(cacheName, {
            val cache = CacheBuilder.newBuilder()
                        .concurrencyLevel(4)
                        .softValues()
                        .expireAfterAccess(30, TimeUnit.MINUTES)
                        .build[String, V]()
            asScalaConcurrentMap[String, V](cache.asMap())
//        })
    }
}

Basically, if I try to add the Guava cache to caches (via the commented out caches.getOrElseUpdate) then the compiler complains with the following:

error: type mismatch;
found   : scala.collection.mutable.ConcurrentMap[String,_$1] where type _$1 <: AnyRef
required: scala.collection.mutable.ConcurrentMap[String,V]
caches.getOrElseUpdate(cacheName, {
2
  • Can you give more of an explanation as to what part of this fails and how? Commented Jan 18, 2012 at 21:45
  • Well, it makes me pretty sure that as a Guava developer unfamiliar with Scala, the problem isn't one I will understand. =/ Commented Jan 19, 2012 at 5:10

1 Answer 1

4

Since you are providing type information when retrieving the cache, it is not necessary to try and maintain the wildcard typing. It's much simpler to type the values to AnyRef and then typecast to V at the end. The following compiles and should help. Also, there is no need to call asScalaConcurrentMap directly since it is well... implicit.

import scala.collection.JavaConversions._
import com.google.common.collect.MapMaker
import java.util.concurrent.TimeUnit
import com.google.common.cache.CacheBuilder
import scala.collection.mutable.ConcurrentMap

trait CacheListener // not sure what this is doing yet.

trait CacheManager {

    def getCache[V <: AnyRef](
            cacheName: String,
            cacheListener: Option[CacheListener] = None): ConcurrentMap[String, V]

}

class LocalCacheManager extends CacheManager {
    private val caches: ConcurrentMap[String, ConcurrentMap[String, AnyRef]] =
                new MapMaker().concurrencyLevel(4).makeMap[String, ConcurrentMap[String, AnyRef]]()
    def getCache[V <: AnyRef](cacheName: String, cacheListener: Option[CacheListener] = None) = 
      caches.getOrElseUpdate(cacheName, {
              CacheBuilder.newBuilder()
                          .concurrencyLevel(4)
                          .softValues()
                          .expireAfterAccess(30, TimeUnit.MINUTES)
                          .asInstanceOf[CacheBuilder[String, AnyRef ]]
                          .build[String, AnyRef ]()
                          .asMap()
              }).asInstanceOf[ConcurrentMap[String, V]]
}
Sign up to request clarification or add additional context in comments.

3 Comments

Is it implicit? Guava is built for Java, not Scala, and I'd think that you'd need to wrap it in some way to make that work. But I'm just a Guava developer, and hence, not a Scala person.
The Scala collection library provides many implicit conversions from Java types to Scala types. asScalaConcurrentMap is made available via the import scala.collection.JavaConversions._ from then on any time a Java collection is encountered where a Scala one is needed it finds the right implicit method to convert it.
Thanks Neil, that worked! Such an obvious and simple solution, I have no idea why I didn't think of it...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.