4

我有一个具有以下签名的函数:

myFunc[T <: AnyRef](arg: T)(implicit m: Manifest[T]) = ???

如果我在编译时不知道参数的确切类型,如何调用此函数?

例如:

val obj: AnyRef = new Foo()    // At compile time obj is defined as AnyRef,
val objClass = obj.getClass    // At runtime I can figure out that it is actually Foo
// Now I would need to call `myFunc[Foo](obj.asInstanceOf[Foo])`,
// but how would I do it without putting [Foo] in the square braces?

我想写一些逻辑上类似于:

myFunc[objClass](obj.asInstanceOf[objClass])

谢谢!

更新:

这个问题是无效的——正如@DaoWen、@Jelmo 和@itsbruce 正确指出的那样,我试图做的事情完全是一派胡言!我只是严重地考虑了这个问题。感谢你们!太糟糕了,我不能接受所有正确的答案:)

因此,问题是由以下情况引起的:

我正在使用Salat库将对象序列化到/从 BSON/JSON 表示。 Salat有一个Grater[T]用于序列化和反序列化的类。从 BSON反序列化的方法调用看起来是这样的:

val foo = grater[Foo].asObject(bson)

在这里,类型参数的作用就很明确了。我当时想做的是使用相同的 Grater序列化我的域模型中的任何实体。所以我写道:

val json = grater[???].toCompactJSON(obj)

我立即急忙进行反思,只是没有看到表面上有明显的解决方案。这是:

grater[Entity].toCompactJSON(obj)  // where Entity...

@Salat trait Entity                // is a root of the domain model hierarchy

有时事情比我们想象的要容易得多!:)

4

4 回答 4

5

似乎在我写这个答案时,问题的作者意识到他不需要Manifest在运行时解析 s 。但是,在我看来,这是我在编写 Yaml [反] 序列化库时成功解决的完全合法的问题,所以我将答案留在这里。


ClassTag可以使用s 甚至s来做你想做的事情TypeTag。我不知道Manifests 因为该 API 已被弃用并且我没有使用它,但我相信使用清单会更容易,因为它们不像新的 Scala 反射那样复杂。仅供参考,Manifest的继任者是TypeTag

假设您有以下功能:

def useClasstag[T: ClassTag](obj: T) = ...

def useTypetag[T: TypeTag](obj: T) = ...

并且您需要将 then withobj: AnyRef作为参数调用,同时提供ClassTagorTypeTagobj.getClass作为隐式参数。

ClassTag是最简单的。您可以ClassTag直接从Class[_]实例创建:

useClasstag(obj)(ClassTag(obj.getClass))

就这样。

TypeTags 更难。您需要使用 Scala 反射从对象中获取一个,然后您必须使用 Scala 反射的一些内部结构。

import scala.reflect.runtime.universe._
import scala.reflect.api
import api.{Universe, TypeCreator}

// Obtain runtime mirror for the class' classloader
val rm = runtimeMirror(obj.getClass.getClassLoader)

// Obtain instance mirror for obj
val im = rm.reflect(obj)

// Get obj's symbol object
val sym = im.symbol

// Get symbol's type signature - that's what you really want!
val tpe = sym.typeSignature

// Now the black magic begins: we create TypeTag manually
// First, make so-called type creator for the type we have just obtained
val tc = new TypeCreator {
  def apply[U <: Universe with Singleton](m: api.Mirror[U]) =
    if (m eq rm) tpe.asInstanceOf[U # Type]
    else throw new IllegalArgumentException(s"Type tag defined in $rm cannot be migrated to other mirrors.")
}
// Next, create a TypeTag using runtime mirror and type creator
val tt = TypeTag[AnyRef](rm, tc)

// Call our method
useTypetag(obj)(tt)

如您所见,这种机器相当复杂。这意味着只有当你真的需要它时才应该使用它,而且正如其他人所说,你真正需要它的情况非常罕见。

于 2013-09-19T19:08:06.047 回答
3

这是行不通的。这样想:你要求编译器为一个直到运行时才知道的类创建一个类 Manifest(在编译时!)。

但是,我觉得您以错误的方式处理问题。真的是你在编译时AnyRef最了解的类型吗?Foo如果是这样的话,你怎么能用它做任何有用的事情呢?(除了为 . 定义的少数方法外,您将无法调用任何方法AnyRef。)

于 2013-09-19T13:53:11.720 回答
2

目前尚不清楚您要达到什么目标,更多的上下文可能会有所帮助。无论如何,这是我的 2 美分。

在这里使用 Manifest 对您没有帮助,因为类型参数需要在编译时知道。我的建议是这样的:

def myFunc[T](arg: AnyRef, klass: Class[T]) = {
  val obj: T = klass.cast(arg)
  //do something with obj... but what?
}

你可以这样称呼它:

myFunc(obj, Foo.class)

请注意,我看不到您如何在内部做一些有用的事情myFunc。在编译时,除了可用于 AnyRef 的方法之外,您不能在 T 类型的对象上调用任何方法。而如果你想使用反射来操作 的参数myFunc,那么就不需要将其强制转换为特定类型。

于 2013-09-19T14:53:22.837 回答
0

这是使用类型安全的 OO 语言的错误方法。如果你需要这样做,你的设计是错误的。

myFunc[T <: AnyRef](arg: T)(implicit m: Manifest[T]) = ???

正如您可能已经发现的那样,这当然是无用的。您可以对可能是任何东西的对象调用什么样的有意义的函数?您不能直接引用其属性或方法。

我想写一些逻辑上类似于:

myFunc[objClass](obj.asInstanceOf[objClass])

为什么?这种事情一般只在非常特殊的情况下才需要。例如,您是否正在编写一个将使用依赖注入的框架?如果您没有对 Scala 的功能进行一些高度技术性的扩展,那么这不是必需的。

我敢打赌你对这个类有更多的了解,因为你说你不知道确切的类型。基于类的 OO 工作方式的一个重要部分是,如果您想对一般类型的对象(包括其所有子类型)执行某些操作,则将该行为放入属于该类的方法中。如果需要,让子类覆盖它。

坦率地说,执行您尝试的方法是在您对类型有足够了解的上下文中调用该函数。

于 2013-09-19T14:40:11.520 回答