不确定如何正确地制定这个(因此,如何查找),但这里是:
我理解应用于对象的方法如何成为函数对象。例如:
case class User(name: String, age: Int)
val user = User("", 0)
user.name _ // () => String
所以,如果我有方法def meth(f: () => String)
,我可以这样做:meth(user.name _)
是否可以定义一个类型,其中包含类 User
的方法?
(更准确地说,从这些方法中获得的函数对象)
换句话说,f
中def meth(f: ???)
的类型是什么,以便能够执行此操作:meth(user.name _)
和meth(user.age _)
谢谢!
答案 0 :(得分:1)
我想这样做的唯一方法就是使用宏。我曾经创建了一个执行以下操作的宏(请注意,当前实现中的某些细节已更改,因此为空)。
case class Metadata(instance: AnyRef, name: String) {
def value = ??? // use reflection to invoke `name` on `instance`
}
object Metadata extends ((AnyRef, String) => Metadata) {
implicit def anyToMetadata(sym: Any): Metadata =
macro MetadataMacro.anyToMetadataImpl
}
private[staticReflection] object MetadataMacro {
def anyToMetadataImpl(c: Context)(sym: c.Expr[Any]): c.Expr[Metadata] = {
import c.universe._
val metadata = // match the tree and create the appropriate metadata instance
c.Expr(metadata)
}
}
在代码中,您可以像这样使用它:
case class User(name:String)
def test(metadata:Metadata) {
println(metadata.name + "->" + metadata.value)
}
val u = User("test")
test(u.name) // name -> test
大约一年前有效的代码可以在这里找到:ee/scala/staticReflection/Metadata.scala。有关macros as they are now的更多信息。
如果这是你想要的,请告诉我,以便我可以看看是否可以将原件转换为工作版本。
我设法让旧的工作。要使用它,只需将代码复制到一个单独的项目(我使用Eclipse),然后通过Java Build Path
链接项目。工作版:
package ee.scala.staticReflection
import scala.language.experimental.macros
import scala.reflect.macros.Context
import language.implicitConversions
case class Metadata(instance: AnyRef, name: String) {
// any comments on how to improve this part are welcome
val runtimeUniverse = scala.reflect.runtime.universe
val mirror = runtimeUniverse.runtimeMirror(getClass.getClassLoader)
val instanceMirror = mirror.reflect(instance)
val method = instanceMirror.symbol.selfType.member(runtimeUniverse newTermName name).asMethod
val methodMirror = instanceMirror.reflectMethod(method)
def value = methodMirror()
}
object Metadata extends ((AnyRef, String) => Metadata) {
implicit def anyToMetadata(sym: Any): Metadata = macro MetadataMacro.anyToMetadataImpl
}
private[staticReflection] object MetadataMacro {
def anyToMetadataImpl(c: Context)(sym: c.Expr[Any]): c.Expr[Metadata] = {
import c.universe._
def createMetadataInstance(select: Select): Tree =
treeBuild.mkMethodCall(
c.mirror.staticModule("ee.scala.staticReflection.Metadata"),
newTermName("apply"),
List(select.qualifier, Literal(Constant(select.name.toString))))
val metadata = sym.tree match {
//normal select
case select: Select => createMetadataInstance(select)
//could be a call using a right associative operator
case Ident(name) =>
c.enclosingMethod.collect {
case ValDef(_, refName, _, select: Select) if refName == name => createMetadataInstance(select)
}
.headOption
.getOrElse(throw new Exception("Could not find ValDef for " + name))
case _ => throw new Exception("Could not create metadata")
}
c.Expr(metadata)
}
}
将所有上述内容应用于原始海报的问题。你可以像这样使用它
case class User(name: String, age: Int)
val user = User("", 0)
def meth(metadata: Metadata) = {
println(metadata.name + "->" + metadata.value)
}
meth(user.name)
meth(user.age)
请注意,解决方案与提议的解决方案不同,但在meth
函数中,您可以执行相同操作(更加均匀)。
答案 1 :(得分:0)
我相信你所寻找的是Scala的“结构类型”(并非真正的Scala创新)。
它捕获非标称类型,但是它在JVM上的实现需要在代码中使用反射,您希望实际访问或调用由结构类型指定的成员。因此,在Scala中使用结构类型会产生非常重要的开销。 (我记得听说过为实现所需的反射派生信息实现某种缓存,但我不知道是否曾经实现过。)