I hope you understand by asking this question that you will loose all compile time guarantees when you rely on runtime data. If this is really what you want then you can rely on Dynamic:
object X extends App {
import scala.language.dynamics
class D(fields: Map[String, Any]) extends Dynamic {
def selectDynamic(str: String): Any =
fields.getOrElse(str, throw new NoSuchFieldException(str))
}
val fields = Map[String, Any]("a" -> true, "b" -> "hello")
val obj = new D(fields)
println(obj.a)
println(obj.b)
}
Dynamic is a compiler feature that translates all field/method calls to a call to the *Dynamic*
methods. Because the compiler can't know anything about your program (how should it?), the return type you get here is Any
and when you call a field/method that does not exist you get an exception at runtime instead of a compile time error. When some fields/methods are known to compile time you can combine Dynamic with macros to get at least some compile time checking (such a method is described in the linked answer).
Beside from that, that is the only syntax you can enable in Scala. If return types are important to you, you can at least add them as type parameter:
object X extends App {
import scala.language.dynamics
class D(fields: Map[String, Any]) extends Dynamic {
def selectDynamic[A : reflect.ClassTag](str: String): A =
fields.get(str) match {
case Some(f: A) => f
case _ => throw new NoSuchFieldException(str)
}
}
val fields = Map[String, Any]("a" -> true, "b" -> "hello")
val obj = new D(fields)
println(obj.a[Boolean])
println(obj.b[String])
}