1

不知道如何正确地制定这个(因此,如何查找),但这里有:

我了解应用于对象的方法如何成为函数对象。例如:

case class User(name: String, age: Int)
val user = User("", 0)
user.name _   // () => String

所以,如果我有一个方法 def meth(f: () => String),我可以这样做:meth(user.name _)

是否可以定义具有方法 作为实例的类型User?(从这些方法中获得的函数对象,更准确地说)

换句话说,为了能够做到这一点, in f的类型是什么: 和def meth(f: ???)meth(user.name _)meth(user.age _)

谢谢!

4

2 回答 2

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。更多关于宏现在的信息。

如果这是您要查找的内容,请告诉我,以便我查看是否可以将原始版本转换为工作版本。


编辑

我设法让旧的工作。要使用它,只需将代码复制粘贴到一个单独的项目中(我使用 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)
  }
}

编辑 2

将上述所有内容应用于原始海报的问题。你可以像这样使用它

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函数内您可以执行相同的操作(更均匀一些)。

于 2013-02-19T21:54:07.943 回答
0

我相信您正在寻找的是 Scala 的“结构类型”(不是真正的 Scala 创新)。

它捕获非标称类型,但它在 JVM 上的实现需要在代码中您要实际访问或调用结构类型指定的成员的位置使用反射。因此,在 Scala 中使用结构类型会产生相当大的开销。(我记得听过有关为所需的反射派生信息实现某种缓存的讨论,但我不知道是否曾经实现过)。

于 2013-02-19T16:40:48.990 回答