Scala map casting issues: type mistmatch

285 views Asked by At

I have the following Scala function:

def processMaps(toProcess : Map[Object,Object]) : Unit = {
  // The 'toProcess' map might have a key named 'innerMap' which is itself a Map[String,String]
  // Compiler Error: type mismatch; found : Object required: (String, String)
  val innerMap : Map[String,String] = if (toProcess.containsKey("innerMap")) Map(toProcess.get("innerMap")) else null

  // Do stuff to 'innerMap'...
}

The problem is that the innerMap declaration produces the following compiler error:

type mismatch; found : Object required: (String, String)

Any idea why and what the fix is?

2

There are 2 answers

0
pedrorijo91 On BEST ANSWER

toProcess.get("innerMap") returns an object, and you are trying to create a Map[String,String] from an object, that makes no sense

you could (but you shouldn't because it can throw exceptions in runtime) force a type cast with:

toProcess.get("innerMap").asInstanceOf[Map[String, String]]

there are several details in your code that are not the best:

  1. a Map[Object,Object] ? you should make the key and values more type specific. Why do you have such a generic type? Are you mixing very different types? That's not idiomatic Scala. At least have a Map[String, Map]
  2. If it's a Scala Map (and not a Java Map), the Map.get method would return an Option. You can do .getOrElse(defaultValue), or instead of Map.get do a Map.getOrElse(key, defaultValue)
  3. Scala avoids the usage of nulls. Instead it uses Option to encapsulate possible unexistent values, and then typical (monad) functions like .map to apply some function/behaviour if the value is present
0
James DW On

Here is another possible solution. Pattern matching is your friend and avoids the asInstanceOf which feels a little brittle.

def processMaps(toProcess : Map[Object,Object]) : Unit = {
    val maybeObject: Option[Object] = toProcess.get("innerMap")
    maybeObject.foreach {
      case inner: Map[String, String] => // do some stuff to inner
        Unit
      case _ => // do nothing
        Unit
    }
}

Or even less code

def processMaps(toProcess : Map[Object,Object]) : Unit = {
    toProcess.get("innerMap").foreach {
       case inner: Map[String, String] => // do some stuff to inner
       case _ => // do nothing
    }
}

A couple of things here.

  1. Using pattern matching is less error prone
  2. foreach is preferred for code that returns Unit.