1
votes

I have a base class, and subclasses MString and MInt from it.

abstract class Base[T](val name:java.lang.String = "") {
    def encode(value:T) : Array[Byte]
}

class MString extends Base[String](name = "string") {
    def encode(value:String) = {
        Array[Byte](10,20)
    }
}

class MInt extends Base[Int](name = "int") {
    def encode(value:Int) = {
        Array[Byte](100,11)
    }
}

I have a map that connects the class name and an instance of it.

val m = Map[String, Base[_]]("string" -> new MString, "int" -> new MInt)

With this map, I was trying to use a string as a key to retrieve the object and invoke the encode method.

println(m("string").encode("A"))
println(m("int").encode(10))

However, I got type mismatch errors:

error: type mismatch;
 found   : String("A")
 required: _$1 where type _$1
    println(m("string").encode("A"))
                               ^
/Users/smcho/code/codetemplate/_pl/scala/_learn/exploration/2016/04/03. type_inference.scala:25: error: type mismatch;
 found   : Int(10)
 required: _$1 where type _$1
    println(m("int").encode(10))

The solution is to specialize with asInstanceOf.

println(m("string").asInstanceOf[MString].encode("A"))
println(m("int").asInstanceOf[MInt].encode(10))

Why is this error? Isn't Scala compiler smart enough to invoke the method from the type of its parameter, for example ("A" -> encode(String) and 10 -> encode(Int))?

This is an issue for my code, as I have another map, and it is used to get the encoding results.

val m2 = Map[String, Any]("int"->10, "string"->"A")

m2 foreach {
 case (key, value) => m(key).encode(value)
}

Any idea to solve this issue?

1
You are retrieving values from a Map[String, Base[_]]. That will be a Base[_] (of unknown type). The compiler cannot know if it will be a MInt or a MString.Thilo

1 Answers

-1
votes

Maybe, I have no choice but to specialize each type that inherits from the Base class.

def encode(input:String, value:Any, m:Map[String, Base[_]]) = {
    if (input.startsWith("string")) 
        m("string").asInstanceOf[MString].encode(value.asInstanceOf[String])
    else if (input.startsWith("int"))
        m("int").asInstanceOf[MInt].encode(value.asInstanceOf[Int])
    else
        throw new Exception("Not supported type")
}

val m2 = Map[String, Any]("int"->10, "string"->"A")

m2 foreach {
    case (key, value) => println(encode(key, value, m).mkString(":"))
}

Running this code will show:

100:11
10:20

We can make the encode function simpler with match/case:

def encode2(input:String, value:Any, m:Map[String, Base[_]]) = {        
    input match {
        case input if input.startsWith("string") 
            => m("string").asInstanceOf[MString].encode(value.asInstanceOf[String])
        case input if input.startsWith("int") 
            => m("int").asInstanceOf[MInt].encode(value.asInstanceOf[Int])
        case _ => throw new Exception("Not supported type")
    }
}