【问题标题】:Function type of all methods of a single class - in Scala单个类的所有方法的函数类型 - 在 Scala 中
【发布时间】:2013-02-04 06:09:29
【问题描述】:

不确定如何正确表述(因此,如何查找),但这里是:

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

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

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

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

为了能够做到这一点,def meth(f: ???) 中的f 的类型应该是什么:meth(user.name _)meth(user.age _)

谢谢!

【问题讨论】:

  • 不太确定您要存档的内容,但也许您可以使用 user.productIterator(并接受 User 作为 meth 的参数)浏览所有字段并对其进行处理?跨度>
  • 既然User 声明了name: Stringage: Int,你觉得你的类型是什么样子的? () => Either[String, Int]? () => Any?
  • 正如@mhs 所说,这两个函数的签名不同是令人困惑的。不确定我是否明白你的意思,但你问的是我之前问过的类似问题吗? stackoverflow.com/questions/13730926/…
  • 这一切都始于必须在可能为空的对象上调用方法(是的,我知道 Option,但我是这段代码的客户)。所以,我反复这样做:val f1 = if (user == nul)"" else user.getFirstName; //等等 。所以我最终得到了这个: def valueOrDefault[T](u: User, f:()=> T, default: T) = if (user == null) default else f() 。我这样称呼它: valueOrDefault(user, user.getFirstName, ""); valueOrDefault(user, user.getAge, 0)。哪个有效,但我想知道是否可以在一种函数类型下抽象 u 的所有方法(而不是 f: () => T)(可能不是)。
  • @teo 那么你可能想要def getName(user: User) = Option(user).map(_.name).getOrElse("") 之类的东西......

标签: function scala methods


【解决方案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)
  }
}

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

【讨论】:

  • 嗨 EECOLOR。哇!这是我第一次接触宏。盯着这段代码呆了一会儿,我想我有点明白这个想法了。除了:这个方法“anyToMetadataImpl()”——它与我的具体问题直接相关吗? (换句话说:我的类用户从它的定义中给出了什么 - 如果有的话?)。或者也许它是通用的,可以在任何情况下使用?非常感谢您抽出宝贵时间!
  • 我已经编辑了我的答案,希望你能看到它是如何相关的。您调用一个方法并提供一个属性(例如user.name),因为它的类型为Metadata 它会触发宏。您将获得Metadata,而不是用户名,它允许您在运行时请求该属性的内容。
  • 确实,我现在明白了。另外,我相信它足够通用,可以与其他类重用,因为我没有看到对 User 类的任何直接引用。伟大的!尽管如此,我认为在我的微不足道的情况下实际使用它有点过分 - 太多了太少了。我会继续接受你的回答,因为它是我问题的实际解决方案。谢谢!
【解决方案2】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-31
    • 2015-12-14
    • 1970-01-01
    • 2013-04-05
    • 1970-01-01
    相关资源
    最近更新 更多