Scala:如何在编译时不知道类型的情况下调用带有类型参数和清单的方法?

2024-02-05

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

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])

谢谢你!

UPDATE:

这个问题是无效的 - 正如@DaoWen、@Jelmo 和@itsbruce 正确指出的那样,我想做的事情完全是无稽之谈!我只是认真地思考了这个问题。 感谢你们!太糟糕了,我不能接受所有正确的答案:)

所以,这个问题是由以下情况引起的:

我在用Salat https://github.com/novus/salat用于将对象序列化为 BSON/JSON 表示形式或从 BSON/JSON 表示形式序列化的库。Salat has an Grater[T]用于序列化和反序列化的类。 该方法调用了反序列化来自 BSON 看起来是这样的:

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

到这里,类型参数的作用就清楚了。我当时想做的是使用同一个刨丝器连载我的域模型中的任何实体。所以我写道:

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

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

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

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

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


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


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

假设您有以下功能:

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

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

然后你需要打电话obj: AnyRef作为参数,同时提供ClassTag or TypeTag for obj.getClass类作为隐式参数。

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

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

就这样。

TypeTag更难。您需要使用 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)

正如你所看到的,这个机器相当复杂。这意味着只有在以下情况下才应该使用它really需要它,并且正如其他人所说,真正需要它的情况非常罕见。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Scala:如何在编译时不知道类型的情况下调用带有类型参数和清单的方法? 的相关文章

随机推荐