メッセージに変数のマージを実装するクラスを Ruby から Scala 2.11 に移植しています。オブジェクトの配列を merge メソッドに渡し、メッセージのテキストで参照されているキーを各オブジェクトで検索させたいと思います。
このコアは、検索する単一のキーと、キーを検索する単一のオブジェクトを受け取る lookUp(key: String, obj: AnyRef) と呼ばれるメソッドです。オブジェクトが Map で、Map のキーが Symbol または String の場合、要求されたキーを Map で探します。それ以外の場合は、オブジェクトにキーと同じ名前のメソッドがあるかどうかを確認し、ある場合はそのメソッドを呼び出します。
既存の Ruby コードでは、これを行うのは簡単です。
def look_up(key, obj)
if obj.respond_to?(:has_key?) && obj.has_key?(key)
obj[key]
elsif obj.respond_to?(key)
obj.send(key)
elsif obj.instance_variable_defined?(ivar = "@#{key}")
obj.instance_variable_get(ivar)
end
end
簡単に実行できるので、Ruby コードは同じ名前のインスタンス変数も探します。私の Scala バージョンでは必ずしもその機能が必要なわけではありません。
私が抱えている問題の 1 つは、私が見つけた例では、呼び出すことができるようにオブジェクトのクラスを事前に知っている必要があることですru.typeOf[MyClass].declaration(ru.TermName("key"))
(where ru
is scala.reflect.runtime.universe
)。
もう 1 つの問題は、このメッセージのマージが 1 分間に何百回も発生する可能性があり、リフレクションが遅く複雑なプロセスのように見えることです。これがすべて計画どおりに機能する場合、オブジェクト タイプごとにリフレクションの結果をキャッシュする可能性があります。
更新: このようなことを考えていましたが、これはやり過ぎですか? または、マップ内のタイプを適切にキャプチャする必要がありますか? また、これはコンパイルされません。Map
、Symbol
およびString
それらのコンテキストに適したタイプではありません。
def lookUp[T](obj: T, key: String)(implicit tag: ru.TypeTag[T]): Option[String] = tag.tpe match {
case ru.TypeRef(a, Map, List(Symbol, _)) => if (obj.contains(Symbol(key))) Some(obj(Symbol(key)).toString) else None
case ru.TypeRef(a, Map, List(String, _)) => if (obj.contains(key)) Some(obj(key).toString) else None
case _ =>
if (/* obj.key() exists */)
// Some(obj.key().toString)
else
None
}
asInstanceOf
更新 2:のようなもので使用できるとは思いもしませんでしたMap[String, _]
。@johny の 2 番目のコード例を使用して、ソリューションを考え出しました。メソッド名をクラスごとに にキャッシュしmutable.HashMap[Class[_], Set[String]]
ます。
def lookUp(obj: AnyRef, key: String): Option[String] = obj match {
case m: Map[_, _] =>
if (m.asInstanceOf[Map[String, _]].contains(key))
extractValue(m.asInstanceOf[Map[String, _]](key))
else if (m.asInstanceOf[Map[Symbol, _]].contains(Symbol(key)))
extractValue(m.asInstanceOf[Map[Symbol, _]](Symbol(key)))
else
None
case _ =>
val klass = obj.getClass
if (!methodsCache.contains(klass))
methodsCache(klass) = klass.getMethods.toList.filter(_.getParameterCount == 0).map(_.getName).toSet
val methodNames = methodsCache(klass)
if (methodsCache(klass).contains(key))
extractValue(klass.getDeclaredMethod(key).invoke(obj))
else
None
}
def extractValue(obj: Any): Option[String] = obj match {
case null | None => None
case Some(x) => Some(x.toString)
case x => Some(x.toString)
}